Skip to content

Commit 1962dbd

Browse files
committed
[docs update]添加问题:Java 线程和操作系统的线程有啥区别?
1 parent e96ea42 commit 1962dbd

File tree

2 files changed

+43
-18
lines changed

2 files changed

+43
-18
lines changed

docs/java/concurrent/java-concurrent-questions-01.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,31 @@ public class MultiThread {
5959

6060
从上面的输出内容可以看出:**一个 Java 程序的运行是 main 线程和多个其他线程同时运行**
6161

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

6489
从 JVM 角度说进程和线程之间的关系。

docs/java/jvm/jvm-intro.md

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 /
330330

331331
设置一个 VM options 的参数
332332

333-
```
333+
```plain
334334
-Xmx20m -Xms5m -XX:+PrintGCDetails
335335
```
336336

@@ -385,23 +385,23 @@ System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 /
385385

386386
### 4.2 调整新生代和老年代的比值
387387

388-
```
388+
```plain
389389
-XX:NewRatio --- 新生代(eden+2\*Survivor)和老年代(不包含永久区)的比值
390390
391391
例如:-XX:NewRatio=4,表示新生代:老年代=1:4,即新生代占整个堆的 1/5。在 Xms=Xmx 并且设置了 Xmn 的情况下,该参数不需要进行设置。
392392
```
393393

394394
### 4.3 调整 Survivor 区和 Eden 区的比值
395395

396-
```
396+
```plain
397397
-XX:SurvivorRatio(幸存代)--- 设置两个 Survivor 区和 eden 的比值
398398
399399
例如:8,表示两个 Survivor:eden=2:8,即一个 Survivor 占年轻代的 1/10
400400
```
401401

402402
### 4.4 设置年轻代和老年代的大小
403403

404-
```
404+
```plain
405405
-XX:NewSize --- 设置年轻代大小
406406
-XX:MaxNewSize --- 设置年轻代最大值
407407
```
@@ -414,15 +414,15 @@ System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 /
414414

415415
在 OOM 时,记得 Dump 出堆,确保可以排查现场问题,通过下面命令你可以输出一个.dump 文件,这个文件可以使用 VisualVM 或者 Java 自带的 Java VisualVM 工具。
416416

417-
```
417+
```plain
418418
-Xmx20m -Xms5m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=你要输出的日志路径
419419
```
420420

421421
一般我们也可以通过编写脚本的方式来让 OOM 出现时给我们报个信,可以通过发送邮件或者重启程序等来解决。
422422

423423
### 4.6 永久区的设置
424424

425-
```
425+
```plain
426426
-XX:PermSize -XX:MaxPermSize
427427
```
428428

@@ -440,7 +440,7 @@ JDK5.0 以后每个线程堆栈大小为 1M,以前每个线程堆栈大小为
440440

441441
#### 4.7.2 设置线程栈的大小
442442

443-
```
443+
```plain
444444
-XXThreadStackSize:
445445
设置线程栈的大小(0 means use default stack size)
446446
```
@@ -453,75 +453,75 @@ JDK5.0 以后每个线程堆栈大小为 1M,以前每个线程堆栈大小为
453453

454454
#### 4.8.1 设置内存页的大小
455455

456-
```
456+
```plain
457457
-XXThreadStackSize:
458458
设置内存页的大小,不可设置过大,会影响Perm的大小
459459
```
460460

461461
#### 4.8.2 设置原始类型的快速优化
462462

463-
```
463+
```plain
464464
-XX:+UseFastAccessorMethods:
465465
设置原始类型的快速优化
466466
```
467467

468468
#### 4.8.3 设置关闭手动 GC
469469

470-
```
470+
```plain
471471
-XX:+DisableExplicitGC:
472472
设置关闭System.gc()(这个参数需要严格的测试)
473473
```
474474

475475
#### 4.8.4 设置垃圾最大年龄
476476

477-
```
477+
```plain
478478
-XX:MaxTenuringThreshold
479479
设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代.对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,加在年轻代即被回收的概率。该参数只有在串行GC时才有效.
480480
```
481481

482482
#### 4.8.5 加快编译速度
483483

484-
```
484+
```plain
485485
-XX:+AggressiveOpts
486486
加快编译速度
487487
```
488488

489489
#### 4.8.6 改善锁机制性能
490490

491-
```
491+
```plain
492492
-XX:+UseBiasedLocking
493493
```
494494

495495
#### 4.8.7 禁用垃圾回收
496496

497-
```
497+
```plain
498498
-Xnoclassgc
499499
```
500500

501501
#### 4.8.8 设置堆空间存活时间
502502

503-
```
503+
```plain
504504
-XX:SoftRefLRUPolicyMSPerMB
505505
设置每兆堆空闲空间中SoftReference的存活时间,默认值是1s。
506506
```
507507

508508
#### 4.8.9 设置对象直接分配在老年代
509509

510-
```
510+
```plain
511511
-XX:PretenureSizeThreshold
512512
设置对象超过多大时直接在老年代分配,默认值是0。
513513
```
514514

515515
#### 4.8.10 设置 TLAB 占 eden 区的比例
516516

517-
```
517+
```plain
518518
-XX:TLABWasteTargetPercent
519519
设置TLAB占eden区的百分比,默认值是1% 。
520520
```
521521

522522
#### 4.8.11 设置是否优先 YGC
523523

524-
```
524+
```plain
525525
-XX:+CollectGen0First
526526
设置FullGC时是否先YGC,默认值是false。
527527
```

0 commit comments

Comments
 (0)