From 65926e5e549b923020f04bb515604d45cbd31c8f Mon Sep 17 00:00:00 2001 From: guide Date: Wed, 23 Jun 2021 20:35:00 +0800 Subject: [PATCH] =?UTF-8?q?Update=202020=E6=9C=80=E6=96=B0Java=E5=B9=B6?= =?UTF-8?q?=E5=8F=91=E5=9F=BA=E7=A1=80=E5=B8=B8=E8=A7=81=E9=9D=A2=E8=AF=95?= =?UTF-8?q?=E9=A2=98=E6=80=BB=E7=BB=93.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...Java并发基础常见面试题总结.md | 59 +++++++++---------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/docs/java/multi-thread/2020最新Java并发基础常见面试题总结.md b/docs/java/multi-thread/2020最新Java并发基础常见面试题总结.md index eab717bb..95e53699 100644 --- a/docs/java/multi-thread/2020最新Java并发基础常见面试题总结.md +++ b/docs/java/multi-thread/2020最新Java并发基础常见面试题总结.md @@ -1,25 +1,25 @@ -- [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-方法) + - [公众号](#公众号) @@ -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 种 ![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) -> 订正(来自[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(运行中)** 状态 。 ![RUNNABLE-VS-RUNNING](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-3/RUNNABLE-VS-RUNNING.png) @@ -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”** 即可免费无套路获取。 ![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png) - -