目录
4.使用场景分析(单例模式双端检锁机制怎么也存在线程安全问题)
可见性,原子性,顺序性是并发编程三要素。volatile能够实现可见性,不能保证原子性,禁止指令重排保证了顺序性,在并发编程种非常常见,面试中也是常客。面试官往往会说聊下对volatile理解,当你回答完第一个问题,接下来将是接连的炮轰。
下面将从并发要素对volatile进行深入理解,懂的原理了,就可以应对相关面试的变种
1.可见性
一.了解JMM可见性需先知道主内存与工作内存的关系
由于 CPU 的处理速度很快,相比之下,内存的速度就显得很慢,所以为了提高 CPU 的整体运行效率,减少空闲时间,在 CPU 和内存之间会有 cache 层,也就是缓存层的存在。虽然缓存的容量比内存小,但是缓存的速度却比内存的速度要快得多,其中 L1 缓存的速度仅次于寄存器的速度。结构示意图如下所示:
(图-1)
Java 作为高级语言,只需要关心 JMM 抽象出来的主内存和工作内存的概念。为了更方便你去理解,可参考下图:
(图-2)
上述的图用我们日常的大白话说就是:多个线程需要去使用共享变量,但是规范是每个线程只能够直接接触到工作内存,无法直接操作主内存,而工作内存中所保存的正是主内存的共享变量的副本,主内存和工作内存之间的通信是由 JMM 控制的。
JMM 有以下规定:
(1)所有的变量都存储在主内存中,同时每个线程拥有自己独立的工作内存,而工作内存中的变量的内容是主内存中该变量的拷贝;
(2)线程不能直接读 / 写主内存中的变量,但可以操作自己工作内存中的变量,然后再同步到主内存中,这样,其他线程就可以看到本次修改;
(3) 主内存是由多个线程所共享的,但线程间不共享各自的工作内存,如果线程间需要通信,则必须借助主内存中转来完成。
二.volatile可见性
通过分析 (图-2)可能引发不同的副本变量在彼此线程不可见行问题,也就是说线程修改 自己内存的副本变量的值其它线程不能立马感应到。 如果主内存中的共享变量加了volatile修饰,可保证线程之间变量修改了彼此可感知到。
如下例子阐释加不加volatile的区别:
不加volatile,修改了change()方法内的值,主内存的变量无法感知到
package com.yang; import java.util.concurrent.TimeUnit; public class TestVolatile { int x = 0; public void change() { this.x = 1; } public static void main(String[] args) { // 资源类 TestVolatile myData = new TestVolatile(); // AAA线程 实现了Runnable接口的,lambda表达式 new Thread(() -> { System.out.println(Thread.currentThread().getName() + "\t come in"); // 线程睡眠3秒,假设在进行运算 |