1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-06-16 18:10:13 +08:00

[docs update]添加问题:Java 线程和操作系统的线程有啥区别?

This commit is contained in:
Guide 2023-10-10 19:25:37 +08:00
parent e96ea425d3
commit 1962dbd0ea
2 changed files with 43 additions and 18 deletions

View File

@ -59,6 +59,31 @@ public class MultiThread {
从上面的输出内容可以看出:**一个 Java 程序的运行是 main 线程和多个其他线程同时运行**。
## Java 线程和操作系统的线程有啥区别?
JDK 1.2 之前Java 线程是基于绿色线程Green Threads实现的这是一种用户级线程用户线程也就是说 JVM 自己模拟了多线程的运行,而不依赖于操作系统。由于绿色线程和原生线程比起来在使用时有一些限制(比如绿色线程不能直接使用操作系统提供的功能如异步 I/O、只能在一个内核线程上运行无法利用多核在 JDK 1.2 及以后Java 线程改为基于原生线程Native Threads实现也就是说 JVM 直接使用操作系统原生的内核级线程(内核线程)来实现 Java 线程,由操作系统内核进行线程的调度和管理。
我们上面提到了用户线程和内核线程,考虑到很多读者不太了解二者的区别,这里简单介绍一下:
- 用户线程:由用户空间程序管理和调度的线程,运行在用户空间(专门给应用程序使用)。
- 内核线程:由操作系统内核管理和调度的线程,运行在内核空间(只有内核程序可以访问)。
顺便简单总结一下用户线程和内核线程的区别和特点:用户线程创建和切换成本低,但不可以利用多核。内核态线程,创建和切换成本高,可以利用多核。
一句话概括 Java 线程和操作系统线程的关系:**现在的 Java 线程的本质其实就是操作系统的线程**。
线程模型是用户线程和内核线程之间的关联方式,常见的线程模型有这三种:
1. 一对一(一个用户线程对应一个内核线程)
2. 多对一(多个用户线程映射到一个内核线程)
3. 多对多(多个用户线程映射到多个内核线程)
![常见的三种线程模型](https://oss.javaguide.cn/github/javaguide/java/concurrent/three-types-of-thread-models.png)
在 Windows 和 Linux 等主流操作系统中Java 线程采用的是一对一的线程模型,也就是一个 Java 线程对应一个系统内核线程。Solaris 系统是一个特例Solaris 系统本身就支持多对多的线程模型HotSpot VM 在 Solaris 上支持多对多和一对一。具体可以参考 R 大的回答: [JVM 中的线程模型是用户级的么?](https://www.zhihu.com/question/23096638/answer/29617153)。
虚拟线程在 JDK 21 顺利转正,关于虚拟线程、平台线程(也就是我们前面提到的 Java 线程)和内核线程三者的关系可以阅读我写的这篇文章:[Java 20 新特性概览](../new-features/java20.md)。
## 请简要描述线程与进程的关系,区别及优缺点?
从 JVM 角度说进程和线程之间的关系。

View File

@ -330,7 +330,7 @@ System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 /
设置一个 VM options 的参数
```
```plain
-Xmx20m -Xms5m -XX:+PrintGCDetails
```
@ -385,7 +385,7 @@ System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 /
### 4.2 调整新生代和老年代的比值
```
```plain
-XX:NewRatio --- 新生代eden+2\*Survivor和老年代不包含永久区的比值
例如:-XX:NewRatio=4表示新生代:老年代=1:4即新生代占整个堆的 1/5。在 Xms=Xmx 并且设置了 Xmn 的情况下,该参数不需要进行设置。
@ -393,7 +393,7 @@ System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 /
### 4.3 调整 Survivor 区和 Eden 区的比值
```
```plain
-XX:SurvivorRatio幸存代--- 设置两个 Survivor 区和 eden 的比值
例如8表示两个 Survivor:eden=2:8即一个 Survivor 占年轻代的 1/10
@ -401,7 +401,7 @@ System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 /
### 4.4 设置年轻代和老年代的大小
```
```plain
-XX:NewSize --- 设置年轻代大小
-XX:MaxNewSize --- 设置年轻代最大值
```
@ -414,7 +414,7 @@ System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 /
在 OOM 时,记得 Dump 出堆,确保可以排查现场问题,通过下面命令你可以输出一个.dump 文件,这个文件可以使用 VisualVM 或者 Java 自带的 Java VisualVM 工具。
```
```plain
-Xmx20m -Xms5m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=你要输出的日志路径
```
@ -422,7 +422,7 @@ System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 /
### 4.6 永久区的设置
```
```plain
-XX:PermSize -XX:MaxPermSize
```
@ -440,7 +440,7 @@ JDK5.0 以后每个线程堆栈大小为 1M以前每个线程堆栈大小为
#### 4.7.2 设置线程栈的大小
```
```plain
-XXThreadStackSize
设置线程栈的大小(0 means use default stack size)
```
@ -453,75 +453,75 @@ JDK5.0 以后每个线程堆栈大小为 1M以前每个线程堆栈大小为
#### 4.8.1 设置内存页的大小
```
```plain
-XXThreadStackSize
设置内存页的大小不可设置过大会影响Perm的大小
```
#### 4.8.2 设置原始类型的快速优化
```
```plain
-XX:+UseFastAccessorMethods
设置原始类型的快速优化
```
#### 4.8.3 设置关闭手动 GC
```
```plain
-XX:+DisableExplicitGC
设置关闭System.gc()(这个参数需要严格的测试)
```
#### 4.8.4 设置垃圾最大年龄
```
```plain
-XX:MaxTenuringThreshold
设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代.对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,加在年轻代即被回收的概率。该参数只有在串行GC时才有效.
```
#### 4.8.5 加快编译速度
```
```plain
-XX:+AggressiveOpts
加快编译速度
```
#### 4.8.6 改善锁机制性能
```
```plain
-XX:+UseBiasedLocking
```
#### 4.8.7 禁用垃圾回收
```
```plain
-Xnoclassgc
```
#### 4.8.8 设置堆空间存活时间
```
```plain
-XX:SoftRefLRUPolicyMSPerMB
设置每兆堆空闲空间中SoftReference的存活时间默认值是1s。
```
#### 4.8.9 设置对象直接分配在老年代
```
```plain
-XX:PretenureSizeThreshold
设置对象超过多大时直接在老年代分配默认值是0。
```
#### 4.8.10 设置 TLAB 占 eden 区的比例
```
```plain
-XX:TLABWasteTargetPercent
设置TLAB占eden区的百分比默认值是1% 。
```
#### 4.8.11 设置是否优先 YGC
```
```plain
-XX:+CollectGen0First
设置FullGC时是否先YGC默认值是false。
```