1
- 点击关注[ 公众号] ( #公众号 ) 及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源 。
1
+ 点击关注[ 公众号] ( #公众号 " 公众号 " ) 及时获取笔主最新更新文章,并可免费领取本文档配套的《Java 面试突击》以及 Java 工程师必备学习资源 。
2
2
3
- <!-- TOC -->
4
3
5
4
- [ Java 并发基础常见面试题总结] ( #java-并发基础常见面试题总结 )
6
5
- [ 1. 什么是线程和进程?] ( #1-什么是线程和进程 )
21
20
- [ 8.2. 如何避免线程死锁?] ( #82-如何避免线程死锁 )
22
21
- [ 9. 说说 sleep() 方法和 wait() 方法区别和共同点?] ( #9-说说-sleep-方法和-wait-方法区别和共同点 )
23
22
- [ 10. 为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?] ( #10-为什么我们调用-start-方法时会执行-run-方法为什么我们不能直接调用-run-方法 )
23
+ - [ 公众号] ( #公众号 )
24
24
25
- <!-- /TOC -->
26
25
27
26
# Java 并发基础常见面试题总结
28
27
@@ -77,7 +76,7 @@ public class MultiThread {
77
76
78
77
### 2.1. 图解进程和线程的关系
79
78
80
- 下图是 Java 内存区域,通过下图我们从 JVM 的角度来说一下线程和进程之间的关系。如果你对 Java 内存区域 (运行时数据区) 这部分知识不太了解的话可以阅读一下这篇文章:[ 《可能是把 Java 内存区域讲的最清楚的一篇文章》] ( < https://github.com/Snailclimb/JavaGuide/blob/3965c02cc0f294b0bd3580df4868d5e396959e2e/Java%E7%9B%B8%E5%85%B3/%E5%8F%AF%E8%83%BD%E6%98%AF%E6%8A%8AJava%E5%86%85%E5%AD%98%E5%8C%BA%E5%9F%9F%E8%AE%B2%E7%9A%84%E6%9C%80%E6%B8%85%E6%A5%9A%E7%9A%84%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0.md > )
79
+ 下图是 Java 内存区域,通过下图我们从 JVM 的角度来说一下线程和进程之间的关系。如果你对 Java 内存区域 (运行时数据区) 这部分知识不太了解的话可以阅读一下这篇文章:[ 《可能是把 Java 内存区域讲的最清楚的一篇文章》] ( https://github.com/Snailclimb/JavaGuide/blob/3965c02cc0f294b0bd3580df4868d5e396959e2e/Java%E7%9B%B8%E5%85%B3/%E5%8F%AF%E8%83%BD%E6%98%AF%E6%8A%8AJava%E5%86%85%E5%AD%98%E5%8C%BA%E5%9F%9F%E8%AE%B2%E7%9A%84%E6%9C%80%E6%B8%85%E6%A5%9A%E7%9A%84%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0.md " 《可能是把 Java 内存区域讲的最清楚的一篇文章》 " )
81
80
82
81
<div align =" center " >
83
82
<img src =" https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-3/JVM运行时数据区域.png " width =" 600px " />
@@ -127,7 +126,7 @@ public class MultiThread {
127
126
128
127
再深入到计算机底层来探讨:
129
128
130
- - ** 单核时代:** 在单核时代多线程主要是为了提高 CPU 和 IO 设备的综合利用率。举个例子:当只有一个线程的时候会导致 CPU 计算时,IO 设备空闲;进行 IO 操作时,CPU 空闲。我们可以简单地说这两者的利用率目前都是 50%左右。但是当有两个线程的时候就不一样了,当一个线程执行 CPU 计算时,另外一个线程可以进行 IO 操作,这样两个的利用率就可以在理想情况下达到 100%了。
129
+ - ** 单核时代:** 在单核时代多线程主要是为了提高 CPU 和 IO 设备的综合利用率。举个例子:当只有一个线程的时候会导致 CPU 计算时,IO 设备空闲;进行 IO 操作时,CPU 空闲。我们可以简单地说这两者的利用率目前都是 50%左右。但是当有两个线程的时候就不一样了,当一个线程执行 CPU 计算时,另外一个线程可以进行 IO 操作,这样两个的利用率就可以在理想情况下达到 100%了。
131
130
- ** 多核时代:** 多核时代多线程主要是为了提高 CPU 利用率。举个例子:假如我们要计算一个复杂的任务,我们只用一个线程的话,CPU 只会一个 CPU 核心被利用到,而创建多个线程就可以让多个 CPU 核心被利用到,这样就提高了 CPU 的利用率。
132
131
133
132
## 5. 使用多线程可能带来什么问题?
@@ -144,23 +143,21 @@ Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种
144
143
145
144
![ Java 线程状态变迁 ] ( https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/19-1-29/Java+%E7%BA%BF%E7%A8%8B%E7%8A%B6%E6%80%81%E5%8F%98%E8%BF%81.png )
146
145
147
-
148
-
149
146
由上图可以看出:线程创建之后它将处于 ** NEW(新建)** 状态,调用 ` start() ` 方法后开始运行,线程这时候处于 ** READY(可运行)** 状态。可运行状态的线程获得了 CPU 时间片(timeslice)后就处于 ** RUNNING(运行)** 状态。
150
147
151
- > 操作系统隐藏 Java 虚拟机(JVM)中的 RUNNABLE 和 RUNNING 状态,它只能看到 RUNNABLE 状态(图源:[ HowToDoInJava] ( https://howtodoinjava.com/ ) :[ Java Thread Life Cycle and Thread States] ( https://howtodoinjava.com/java/multi-threading/java-thread-life-cycle-and-thread-states/ ) ),所以 Java 系统一般将这两个状态统称为 ** RUNNABLE(运行中)** 状态 。
148
+ > 操作系统隐藏 Java 虚拟机(JVM)中的 RUNNABLE 和 RUNNING 状态,它只能看到 RUNNABLE 状态(图源:[ HowToDoInJava] ( https://howtodoinjava.com/ " HowToDoInJava " ) :[ Java Thread Life Cycle and Thread States] ( https://howtodoinjava.com/java/multi-threading/java-thread-life-cycle-and-thread-states/ " Java Thread Life Cycle and Thread States " ) ),所以 Java 系统一般将这两个状态统称为 ** RUNNABLE(运行中)** 状态 。
152
149
153
150
![ RUNNABLE-VS-RUNNING] ( https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-3/RUNNABLE-VS-RUNNING.png )
154
151
155
- 当线程执行 ` wait() ` 方法之后,线程进入 ** WAITING(等待)** 状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态,而 ** TIME_WAITING(超时等待)** 状态相当于在等待状态的基础上增加了超时限制,比如通过 ` sleep(long millis) ` 方法或 ` wait(long millis) ` 方法可以将 Java 线程置于 TIMED WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 ** BLOCKED(阻塞)** 状态。线程在执行 Runnable 的` run() ` 方法之后将会进入到 ** TERMINATED(终止)** 状态。
152
+ 当线程执行 ` wait() ` 方法之后,线程进入 ** WAITING(等待)** 状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态,而 ** TIME_WAITING(超时等待)** 状态相当于在等待状态的基础上增加了超时限制,比如通过 ` sleep(long millis) ` 方法或 ` wait(long millis) ` 方法可以将 Java 线程置于 TIMED WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 ** BLOCKED(阻塞)** 状态。线程在执行 Runnable 的` run() ` 方法之后将会进入到 ** TERMINATED(终止)** 状态。
156
153
157
154
## 7. 什么是上下文切换?
158
155
159
156
多线程编程中一般线程的个数都大于 CPU 核心的个数,而一个 CPU 核心在任意时刻只能被一个线程使用,为了让这些线程都能得到有效执行,CPU 采取的策略是为每个线程分配时间片并轮转的形式。当一个线程的时间片用完的时候就会重新处于就绪状态让给其他线程使用,这个过程就属于一次上下文切换。
160
157
161
158
概括来说就是:当前任务在执行完 CPU 时间片切换到另一个任务之前会先保存自己的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态。** 任务从保存到再加载的过程就是一次上下文切换** 。
162
159
163
- 上下文切换通常是计算密集型的。也就是说,它需要相当可观的处理器时间,在每秒几十上百次的切换中,每次切换都需要纳秒量级的时间。所以,上下文切换对系统来说意味着消耗大量的 CPU 时间,事实上,可能是操作系统中时间消耗最大的操作。
160
+ 上下文切换通常是计算密集型的。也就是说,它需要相当可观的处理器时间,在每秒几十上百次的切换中,每次切换都需要纳秒量级的时间。所以,上下文切换对系统来说意味着消耗大量的 CPU 时间,事实上,可能是操作系统中时间消耗最大的操作。
164
161
165
162
Linux 相比与其他操作系统(包括其他类 Unix 系统)有很多的优点,其中有一项就是,其上下文切换和模式切换的时间消耗非常少。
166
163
@@ -224,7 +221,7 @@ Thread[线程 1,5,main]waiting get resource2
224
221
Thread[线程 2,5,main]waiting get resource1
225
222
```
226
223
227
- 线程 A 通过 synchronized (resource1) 获得 resource1 的监视器锁,然后通过` Thread.sleep(1000);` 让线程 A 休眠 1s 为的是让线程 B 得到执行然后获取到 resource2 的监视器锁。线程 A 和线程 B 休眠结束了都开始企图请求获取对方的资源,然后这两个线程就会陷入互相等待的状态,这也就产生了死锁。上面的例子符合产生死锁的四个必要条件。
224
+ 线程 A 通过 synchronized (resource1) 获得 resource1 的监视器锁,然后通过` Thread.sleep(1000); ` 让线程 A 休眠 1s 为的是让线程 B 得到执行然后获取到 resource2 的监视器锁。线程 A 和线程 B 休眠结束了都开始企图请求获取对方的资源,然后这两个线程就会陷入互相等待的状态,这也就产生了死锁。上面的例子符合产生死锁的四个必要条件。
228
225
229
226
学过操作系统的朋友都知道产生死锁必须具备以下四个条件:
230
227
@@ -294,8 +291,7 @@ Process finished with exit code 0
294
291
- 两者最主要的区别在于:** sleep 方法没有释放锁,而 wait 方法释放了锁** 。
295
292
- 两者都可以暂停线程的执行。
296
293
- Wait 通常被用于线程间交互/通信,sleep 通常被用于暂停执行。
297
- - wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify() 或者 notifyAll() 方法。sleep() 方法执行完成后,线程会自动苏醒。或者可以使用wait(long timeout)超时后线程会自动苏醒。
298
-
294
+ - wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify() 或者 notifyAll() 方法。sleep() 方法执行完成后,线程会自动苏醒。或者可以使用 wait(long timeout)超时后线程会自动苏醒。
299
295
300
296
## 10. 为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?
301
297
@@ -309,8 +305,8 @@ new 一个 Thread,线程进入了新建状态;调用 start() 方法,会启
309
305
310
306
如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。
311
307
312
- ** 《Java面试突击 》:** 由本文档衍生的专为面试而生的《Java面试突击 》V2.0 PDF 版本[ 公众号] ( #公众号 ) 后台回复 ** "面试突击"** 即可免费领取!
308
+ ** 《Java 面试突击 》:** 由本文档衍生的专为面试而生的《Java 面试突击 》V2.0 PDF 版本[ 公众号] ( #公众号 " 公众号 " ) 后台回复 ** "面试突击"** 即可免费领取!
313
309
314
- ** Java工程师必备学习资源 :** 一些Java工程师常用学习资源公众号后台回复关键字 ** “1”** 即可免费无套路获取。
310
+ ** Java 工程师必备学习资源 :** 一些 Java 工程师常用学习资源公众号后台回复关键字 ** “1”** 即可免费无套路获取。
315
311
316
312
![ 我的公众号] ( https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png )
0 commit comments