- 并发的三大特性
- JMM工作内存和主内存关系
- 多线程之间通信
- volatile能保证可见性和有序性
- CAS
并发三大特性
可见性
内存屏障:立即回写 缓存失效
lock前缀指令不是内存屏障的指令,但是有内存屏障的效果 缓存失效
volatile OrderAccess::storeLoad JVM内存屏障
显示调用内存屏障storeFence()
Thread.yield() 释放时间片 上下文切换 加载上下文时更新数据
synchronized 内存屏障
wait
thread.sleep(1) 调用写屏障 底层调用park实现(操作系统库函数pthread_cond_timedwait)1:50
Integer 的value是final final修饰变量也要保证可见性 也从主存重新加载
- 总结:java中可见性如何保证
- 内存屏障storeLoad ==>x86 lock替代了mfence
- 上下文切换
- java层面
- volatile 锁机制
当前线程对共享变量的操作会存在读不到,或者不能立即读到另一个线程对此变量的写操作
- volatile 锁机制
lock 硬件层面扩展 JMM为什么选择共享内存模型
JMM模型 java共享线程模型
缓存锁定
MESI 缓存行 64byte
M:修改
E:独占
S:共享
I:无效
lock前缀指令 立即刷回主存
2种情况保证不了:
- 跨缓存行
- 早期处理器没有实现缓存一致性协议
(总线锁定:串行 回到了单核时代) lock前缀+lock#
重排序
as-if-serial
不管怎么重排序(编译器和处理器为了提高并行度),程序的执行结果不能被改变
编译器、runtime和
happen-before
jvm会对代码进行编译优化,指令出现重排序的情况,为了避免编译优化对并发编程安全性影响,需要happens-before规则定义一些禁止编译优化的场景,保障比那个发编程的正确性
voliate规则:threadB对flag的写操作会happen-before threadA对flag的读操作
管程中的锁规则:unlock操作先行发生于后面对同一个锁的lock操作;
线程规则:Thread对象的start方法先于发生于此线程的每个动作
DCL为什么要使用volatile
双重判断 临界区重排序
- 开辟一片内存空间
- 对象初始化
- myInstance指向内存空间的地址
2 3指令重排时结果就会有问题
myInstance = newSingletomFactory();
volatile内存语义
可见性:读的时候总能看到最后的写入
有序性:构造内存屏障来进制指令重排序来保障有序性
保证32位对long和double的原子性 java规范17章第七条
锁机制synchronized详解
synchronized线程安全的阻塞式解决方案,对象锁,采用互斥的方式让同一时刻最多只有一个线程持有对象锁,其他线程在想获取锁时只能被阻塞住。这样就能保证持有锁的线程能够安全执行操作共享资源。
原理
在java中,每一个对象有且仅有一个同步锁。同步锁依赖对象而存在
调用对象的synchronized方法时,就获取了对象的同步锁。
不同线程对同步锁的访问是互斥的
加锁和释放锁都是由字节码中管程控制的
可以用在
- 实例方法
- 实例方法的代码块
- 静态方法
- 静态方法中的代码块
- synchrnized修饰实例方法,如果一个线程获得对象锁,线程在调用其他用synchronized修饰的方法时会直接执行,不会等待继续获取锁
- sychronized修饰静态方法对类进行加锁,限制多线程同时访问该类的所有实例