背景
编写一个单例的实现。这里采用一个双重检查方式。
image.png
发现是有问题的。主要是编译优化导致new 对象的顺序和可见性问题。
问题修复 :只需要再单例对象 加上 volatile 修饰即可。
分析背后的原因
多线程编程中主要核心关注如下三个点 :
可见性
缓存带来的原子性 (硬件的坑)
原子性
线程切换带来的原子性
有序问题
image.png
这里给出一个volatile 修饰的对象 new 对象在编译后的执行字节码流程,可以看出没有做编译优化和顺序重排。
JAVA并发常见的内存模型
valatile :禁
这条规则是指一个valatile 变量的写操作
Happens-Before 于后续对这个 valatile变量的读操作
le变量的读操作
final :
编译优化更好点 ,生而不变,可以使劲的优化
传递性:
Happens-Before: 前面的操作结果对后续的操作是可见的
x = 7 y = 8 在多线程中,
传递性 是指 :
线程A:写 X =7 Y =8 成功
线程B:如果读到 y = 8 ,
那么他读到的X 肯定是7
Java 采用的是管程技术,synchronized 关键字及 wait()、notify()、notifyAll() 这三个方法都是管程的组成部分。
而管程和信号量是等价的,所谓等价指的是用管程能够实现信号量,也能用信号量实现管程。但是管程在利用OOP的封装特性解决了信号量在工程实践上的复杂性问题,因此java采用管理机制。
因此java
就是将共享变量及其对共享变量的操作统一封装起来。在下图中,管程 X 将共享变量 queue 这个队列和相关的操作入队 enq()、出队 deq() 都封装起来了;线程 A 和线程 B 如果想访问共享变量 queue,只能通过调用管程提供的 enq()、deq() 方法来实现;enq()、deq() 保证互斥性,只允许一个线程进入管程。管程模型和面向对象高度契合的。
image.png
管程如何解决线程间的同步问题呢?
Java 参考了 MESA 模型,语言内置的管程(synchronized)对 MESA 模型进行了精简。MESA 模型中,条件变量可以有多个,Java 语言内置的管程里只有一个条件变量。具体如下图所示。
image.png
JAVA SDK并发包通过Lock 和Condition两个接口’又’实现了一次管程。LOCK 解决互斥,Condition解决同步问题
重复造轮子
1.能够响应终端
2.支持超时
3. 非阻塞获取锁
image.png
轮子的理由:
1.能够响应终端
2.支持超时
先找到一个 阻塞的线程 看执行栈信息
image.png
image.png