mirror of
https://github.com/Snailclimb/JavaGuide
synced 2025-08-01 16:28:03 +08:00
Update 2020最新Java并发基础常见面试题总结.md
This commit is contained in:
parent
5da52168b5
commit
65926e5e54
@ -1,25 +1,25 @@
|
||||
<!-- TOC -->
|
||||
|
||||
- [Java 并发基础常见面试题总结](#java-并发基础常见面试题总结)
|
||||
- [1. 什么是线程和进程?](#1-什么是线程和进程)
|
||||
- [1.1. 何为进程?](#11-何为进程)
|
||||
- [1.2. 何为线程?](#12-何为线程)
|
||||
- [2. 请简要描述线程与进程的关系,区别及优缺点?](#2-请简要描述线程与进程的关系区别及优缺点)
|
||||
- [2.1. 图解进程和线程的关系](#21-图解进程和线程的关系)
|
||||
- [2.2. 程序计数器为什么是私有的?](#22-程序计数器为什么是私有的)
|
||||
- [2.3. 虚拟机栈和本地方法栈为什么是私有的?](#23-虚拟机栈和本地方法栈为什么是私有的)
|
||||
- [2.4. 一句话简单了解堆和方法区](#24-一句话简单了解堆和方法区)
|
||||
- [3. 说说并发与并行的区别?](#3-说说并发与并行的区别)
|
||||
- [4. 为什么要使用多线程呢?](#4-为什么要使用多线程呢)
|
||||
- [5. 使用多线程可能带来什么问题?](#5-使用多线程可能带来什么问题)
|
||||
- [6. 说说线程的生命周期和状态?](#6-说说线程的生命周期和状态)
|
||||
- [7. 什么是上下文切换?](#7-什么是上下文切换)
|
||||
- [8. 什么是线程死锁?如何避免死锁?](#8-什么是线程死锁如何避免死锁)
|
||||
- [8.1. 认识线程死锁](#81-认识线程死锁)
|
||||
- [8.2. 如何避免线程死锁?](#82-如何避免线程死锁)
|
||||
- [9. 说说 sleep() 方法和 wait() 方法区别和共同点?](#9-说说-sleep-方法和-wait-方法区别和共同点)
|
||||
- [10. 为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?](#10-为什么我们调用-start-方法时会执行-run-方法为什么我们不能直接调用-run-方法)
|
||||
- [公众号](#公众号)
|
||||
- [Java 并发基础常见面试题总结](#Java-并发基础常见面试题总结)
|
||||
- [1. 什么是线程和进程?](#1-什么是线程和进程)
|
||||
- [1.1. 何为进程?](#11-何为进程)
|
||||
- [1.2. 何为线程?](#12-何为线程)
|
||||
- [2. 请简要描述线程与进程的关系,区别及优缺点?](#2-请简要描述线程与进程的关系区别及优缺点)
|
||||
- [2.1. 图解进程和线程的关系](#21-图解进程和线程的关系)
|
||||
- [2.2. 程序计数器为什么是私有的?](#22-程序计数器为什么是私有的)
|
||||
- [2.3. 虚拟机栈和本地方法栈为什么是私有的?](#23-虚拟机栈和本地方法栈为什么是私有的)
|
||||
- [2.4. 一句话简单了解堆和方法区](#24-一句话简单了解堆和方法区)
|
||||
- [3. 说说并发与并行的区别?](#3-说说并发与并行的区别)
|
||||
- [4. 为什么要使用多线程呢?](#4-为什么要使用多线程呢)
|
||||
- [5. 使用多线程可能带来什么问题?](#5-使用多线程可能带来什么问题)
|
||||
- [6. 说说线程的生命周期和状态?](#6-说说线程的生命周期和状态)
|
||||
- [7. 什么是上下文切换?](#7-什么是上下文切换)
|
||||
- [8. 什么是线程死锁?如何避免死锁?](#8-什么是线程死锁如何避免死锁)
|
||||
- [8.1. 认识线程死锁](#81-认识线程死锁)
|
||||
- [8.2. 如何避免线程死锁?](#82-如何避免线程死锁)
|
||||
- [9. 说说 sleep() 方法和 wait() 方法区别和共同点?](#9-说说-sleep-方法和-wait-方法区别和共同点)
|
||||
- [10. 为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?](#10-为什么我们调用-start-方法时会执行-run-方法为什么我们不能直接调用-run-方法)
|
||||
- [公众号](#公众号)
|
||||
|
||||
<!-- /TOC -->
|
||||
|
||||
@ -126,11 +126,8 @@ public class MultiThread {
|
||||
|
||||
再深入到计算机底层来探讨:
|
||||
|
||||
- **单核时代**: 在单核时代多线程主要是为了提高单进程利用CPU和IO系统的效率。 当我们请求IO的时候,如果java进程中只有一个线程,此线程被IO阻塞则整个进程被阻塞。CPU和IO设备只有一个在运行,那么可以简单地说系统整体效率只有50%。当使用多线程的时候,一个线程被IO阻塞,其他线程还可以继续使用CPU。从而提高了java进程利用系统资源的整体效率。
|
||||
|
||||
注意:此种情况是指的计算机只有一个CPU核心,并且假设只运行了一个java进程的情况,多进程的时候,操作系统会调度不同进程占用CPU,也不会存在浪费CPU的问题,只不过是因为大型项目中,作为服务器运行的机器中一般不会运行太多无关进程,所以才可作此假设。
|
||||
|
||||
- **多核时代**: 多核时代多线程主要是为了提高进程利用多核CPU的能力。举个例子:假如我们要计算一个复杂的任务,我们只用一个线程的话,不论系统有几个CPU核心,都只会有一个 CPU 核心被利用到。而创建多个线程,这些线程可以被映射到底层多个CPU上执行,在任务中的多个线程没有资源竞争的情况下,任务执行的效率会有显著性的提高,约等于(单核时执行时间/CPU核心数)。
|
||||
- **单核时代**: 在单核时代多线程主要是为了提高单进程利用 CPU 和 IO 系统的效率。 假设只运行了一个 Java 进程的情况,当我们请求 IO 的时候,如果 Java 进程中只有一个线程,此线程被 IO 阻塞则整个进程被阻塞。CPU 和 IO 设备只有一个在运行,那么可以简单地说系统整体效率只有 50%。当使用多线程的时候,一个线程被 IO 阻塞,其他线程还可以继续使用 CPU。从而提高了 Java 进程利用系统资源的整体效率。
|
||||
- **多核时代**: 多核时代多线程主要是为了提高进程利用多核 CPU 的能力。举个例子:假如我们要计算一个复杂的任务,我们只用一个线程的话,不论系统有几个 CPU 核心,都只会有一个 CPU 核心被利用到。而创建多个线程,这些线程可以被映射到底层多个 CPU 上执行,在任务中的多个线程没有资源竞争的情况下,任务执行的效率会有显著性的提高,约等于(单核时执行时间/CPU 核心数)。
|
||||
|
||||
## 5. 使用多线程可能带来什么问题?
|
||||
|
||||
@ -146,11 +143,11 @@ Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种
|
||||
|
||||

|
||||
|
||||
> 订正(来自[issue736](https://github.com/Snailclimb/JavaGuide/issues/736)):原图中 wait到 runnable状态的转换中,`join`实际上是`Thread`类的方法,但这里写成了`Object`。
|
||||
> 订正(来自[issue736](https://github.com/Snailclimb/JavaGuide/issues/736)):原图中 wait 到 runnable 状态的转换中,`join`实际上是`Thread`类的方法,但这里写成了`Object`。
|
||||
|
||||
由上图可以看出:线程创建之后它将处于 **NEW(新建)** 状态,调用 `start()` 方法后开始运行,线程这时候处于 **READY(可运行)** 状态。可运行状态的线程获得了 CPU 时间片(timeslice)后就处于 **RUNNING(运行)** 状态。
|
||||
|
||||
> 操作系统隐藏 Java 虚拟机(JVM)中的 READY 和 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(运行中)** 状态 。
|
||||
> 操作系统隐藏 Java 虚拟机(JVM)中的 READY 和 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(运行中)** 状态 。
|
||||
|
||||

|
||||
|
||||
@ -239,7 +236,7 @@ Thread[线程 2,5,main]waiting get resource1
|
||||
|
||||
**如何预防死锁?** 破坏死锁的产生的必要条件即可:
|
||||
|
||||
1. **破坏请求与保持条件** :一次性申请所有的资源。
|
||||
1. **破坏请求与保持条件** :一次性申请所有的资源。
|
||||
2. **破坏不剥夺条件** :占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
|
||||
3. **破坏循环等待条件** :靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。
|
||||
|
||||
@ -294,9 +291,9 @@ Process finished with exit code 0
|
||||
|
||||
## 10. 为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?
|
||||
|
||||
这是另一个非常经典的 java 多线程面试问题,而且在面试中会经常被问到。很简单,但是很多人都会答不上来!
|
||||
这是另一个非常经典的 Java 多线程面试问题,而且在面试中会经常被问到。很简单,但是很多人都会答不上来!
|
||||
|
||||
new 一个 Thread,线程进入了新建状态。调用 `start()`方法,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。 `start()` 会执行线程的相应准备工作,然后自动执行 ` run() ` 方法的内容,这是真正的多线程工作。 但是,直接执行 `run()` 方法,会把 `run()` 方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。
|
||||
new 一个 Thread,线程进入了新建状态。调用 `start()`方法,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。 `start()` 会执行线程的相应准备工作,然后自动执行 `run()` 方法的内容,这是真正的多线程工作。 但是,直接执行 `run()` 方法,会把 `run()` 方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。
|
||||
|
||||
**总结: 调用 `start()` 方法方可启动线程并使线程进入就绪状态,直接执行 `run()` 方法的话不会以多线程的方式执行。**
|
||||
|
||||
@ -309,5 +306,3 @@ new 一个 Thread,线程进入了新建状态。调用 `start()`方法,会
|
||||
**Java 工程师必备学习资源:** 一些 Java 工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。
|
||||
|
||||

|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user