提交 6990d76f 编写于 作者: S SnailClimb

Update:optimize content

上级 bb148aad
点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。
<!-- TOC --> <!-- TOC -->
- [Java 并发进阶常见面试题总结](#java-并发进阶常见面试题总结) - [Java 并发进阶常见面试题总结](#java-并发进阶常见面试题总结)
...@@ -116,7 +118,7 @@ public class SynchronizedDemo { ...@@ -116,7 +118,7 @@ public class SynchronizedDemo {
通过 JDK 自带的 javap 命令查看 SynchronizedDemo 类的相关字节码信息:首先切换到类的对应目录执行 `javac SynchronizedDemo.java` 命令生成编译后的 .class 文件,然后执行`javap -c -s -v -l SynchronizedDemo.class` 通过 JDK 自带的 javap 命令查看 SynchronizedDemo 类的相关字节码信息:首先切换到类的对应目录执行 `javac SynchronizedDemo.java` 命令生成编译后的 .class 文件,然后执行`javap -c -s -v -l SynchronizedDemo.class`
![synchronized 关键字原理](https://user-gold-cdn.xitu.io/2018/10/26/166add616a292bcf?w=917&h=633&f=png&s=21863) ![synchronized关键字原理](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/synchronized关键字原理.png)
从上面我们可以看出: 从上面我们可以看出:
...@@ -133,7 +135,7 @@ public class SynchronizedDemo2 { ...@@ -133,7 +135,7 @@ public class SynchronizedDemo2 {
``` ```
![synchronized 关键字原理](https://user-gold-cdn.xitu.io/2018/10/26/166add6169fc206d?w=875&h=421&f=png&s=16114) ![synchronized关键字原理](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/synchronized关键字原理2.png)
synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是 ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法,JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。 synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是 ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法,JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。
...@@ -176,13 +178,13 @@ synchronized 是依赖于 JVM 实现的,前面我们也讲到了 虚拟机团 ...@@ -176,13 +178,13 @@ synchronized 是依赖于 JVM 实现的,前面我们也讲到了 虚拟机团
在 JDK1.2 之前,Java的内存模型实现总是从**主存**(即共享内存)读取变量,是不需要进行特别的注意的。而在当前的 Java 内存模型下,线程可以把变量保存**本地内存**比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成**数据的不一致** 在 JDK1.2 之前,Java的内存模型实现总是从**主存**(即共享内存)读取变量,是不需要进行特别的注意的。而在当前的 Java 内存模型下,线程可以把变量保存**本地内存**比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成**数据的不一致**
![数据的不一致](https://user-gold-cdn.xitu.io/2018/10/30/166c46ede4423ba2?w=273&h=166&f=jpeg&s=7268) ![数据不一致](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/数据不一致.png)
要解决这个问题,就需要把变量声明为**volatile**,这就指示 JVM,这个变量是不稳定的,每次使用它都到主存中进行读取。 要解决这个问题,就需要把变量声明为**volatile**,这就指示 JVM,这个变量是不稳定的,每次使用它都到主存中进行读取。
说白了, **volatile** 关键字的主要作用就是保证变量的可见性然后还有一个作用是防止指令重排序。 说白了, **volatile** 关键字的主要作用就是保证变量的可见性然后还有一个作用是防止指令重排序。
![volatile关键字的可见性](https://user-gold-cdn.xitu.io/2018/10/30/166c46ede4b9f501?w=474&h=238&f=jpeg&s=9942) ![volatile关键字的可见性](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/volatile关键字的可见性.png)
### 2.2. 说说 synchronized 关键字和 volatile 关键字的区别 ### 2.2. 说说 synchronized 关键字和 volatile 关键字的区别
...@@ -325,7 +327,7 @@ ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; ...@@ -325,7 +327,7 @@ ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
`ThreadLocalMap``ThreadLocal`的静态内部类。 `ThreadLocalMap``ThreadLocal`的静态内部类。
![ThreadLocal内部类](https://ws1.sinaimg.cn/large/006rNwoDgy1g2f47u9li2j30ka08cq43.jpg) ![ThreadLocal内部类](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/ThreadLocal内部类.png)
### 3.4. ThreadLocal 内存泄露问题 ### 3.4. ThreadLocal 内存泄露问题
...@@ -383,7 +385,7 @@ ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; ...@@ -383,7 +385,7 @@ ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
> - **CachedThreadPool 和 ScheduledThreadPool** : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。 > - **CachedThreadPool 和 ScheduledThreadPool** : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。
**方式一:通过构造方法实现** **方式一:通过构造方法实现**
![通过构造方法实现](https://user-gold-cdn.xitu.io/2018/10/30/166c4a5baac923e9?w=925&h=158&f=jpeg&s=29190) ![ThreadPoolExecutor构造方法](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/ThreadPoolExecutor构造方法.png)
**方式二:通过Executor 框架的工具类Executors来实现** **方式二:通过Executor 框架的工具类Executors来实现**
我们可以创建三种类型的ThreadPoolExecutor: 我们可以创建三种类型的ThreadPoolExecutor:
...@@ -392,7 +394,7 @@ ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; ...@@ -392,7 +394,7 @@ ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
- **CachedThreadPool:** 该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。 - **CachedThreadPool:** 该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。
对应Executors工具类中的方法如图所示: 对应Executors工具类中的方法如图所示:
![通过Executor 框架的工具类Executors来实现](https://user-gold-cdn.xitu.io/2018/10/30/166c4a5baa9ca5e9?w=645&h=222&f=jpeg&s=31710) ![Executor框架的工具类](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/Executor框架的工具类.png)
## 5. Atomic 原子类 ## 5. Atomic 原子类
...@@ -405,7 +407,7 @@ Atomic 翻译成中文是原子的意思。在化学上,我们知道原子是 ...@@ -405,7 +407,7 @@ Atomic 翻译成中文是原子的意思。在化学上,我们知道原子是
并发包 `java.util.concurrent` 的原子类都存放在`java.util.concurrent.atomic`下,如下图所示。 并发包 `java.util.concurrent` 的原子类都存放在`java.util.concurrent.atomic`下,如下图所示。
![ JUC 原子类概览](https://user-gold-cdn.xitu.io/2018/10/30/166c4ac08d4c5547?w=317&h=367&f=png&s=13267) ![JUC原子类概览](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/JUC原子类概览.png)
### 5.2. JUC 包中的原子类是哪4类? ### 5.2. JUC 包中的原子类是哪4类?
...@@ -504,7 +506,7 @@ CAS的原理是拿期望的值和原本的一个值作比较,如果相同则 ...@@ -504,7 +506,7 @@ CAS的原理是拿期望的值和原本的一个值作比较,如果相同则
AQS的全称为(AbstractQueuedSynchronizer),这个类在java.util.concurrent.locks包下面。 AQS的全称为(AbstractQueuedSynchronizer),这个类在java.util.concurrent.locks包下面。
![enter image description here](https://user-gold-cdn.xitu.io/2018/10/30/166c4bb575d4a690?w=317&h=338&f=png&s=14122) ![AQS类](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/AQS类.png)
AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的ReentrantLock,Semaphore,其他的诸如ReentrantReadWriteLock,SynchronousQueue,FutureTask等等皆是基于AQS的。当然,我们自己也能利用AQS非常轻松容易地构造出符合我们自己需求的同步器。 AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的ReentrantLock,Semaphore,其他的诸如ReentrantReadWriteLock,SynchronousQueue,FutureTask等等皆是基于AQS的。当然,我们自己也能利用AQS非常轻松容易地构造出符合我们自己需求的同步器。
...@@ -518,8 +520,6 @@ AQS 原理这部分参考了部分博客,在5.2节末尾放了链接。 ...@@ -518,8 +520,6 @@ AQS 原理这部分参考了部分博客,在5.2节末尾放了链接。
#### 6.2.1. AQS 原理概览 #### 6.2.1. AQS 原理概览
**AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。** **AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。**
> CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配。 > CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配。
...@@ -527,7 +527,7 @@ AQS 原理这部分参考了部分博客,在5.2节末尾放了链接。 ...@@ -527,7 +527,7 @@ AQS 原理这部分参考了部分博客,在5.2节末尾放了链接。
看个AQS(AbstractQueuedSynchronizer)原理图: 看个AQS(AbstractQueuedSynchronizer)原理图:
![enter image description here](https://user-gold-cdn.xitu.io/2018/10/30/166c4bbe4a9c5ae7?w=852&h=401&f=png&s=21797) ![AQS原理图](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/AQS原理图.png)
AQS使用一个int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。AQS使用CAS对该同步状态进行原子操作实现对其值的修改。 AQS使用一个int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。AQS使用CAS对该同步状态进行原子操作实现对其值的修改。
...@@ -613,3 +613,13 @@ tryReleaseShared(int)//共享方式。尝试释放资源,成功则返回true ...@@ -613,3 +613,13 @@ tryReleaseShared(int)//共享方式。尝试释放资源,成功则返回true
- http://www.cnblogs.com/waterystone/p/4920797.html - http://www.cnblogs.com/waterystone/p/4920797.html
- https://www.cnblogs.com/chengxiao/archive/2017/07/24/7141160.html - https://www.cnblogs.com/chengxiao/archive/2017/07/24/7141160.html
- <https://www.journaldev.com/1076/java-threadlocal-example> - <https://www.journaldev.com/1076/java-threadlocal-example>
## 公众号
如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。
**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"面试突击"** 即可免费领取!
**Java工程师必备学习资源:** 一些Java工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。
![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png)
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册