volatile是java虚拟机提供的轻量级的同步机制。volatile可以保证可见性和禁止指令重排。
对被volatile修饰的共享变量总是对所有线程是可见的。当volatile被修饰的共享变量被修改后,修改后的值立马就会被其它线程读到。这就是可见性。
禁止指令重排,保证单个volatile变量的读/写具有原子性。但是对于volatile++
这种复合操作不具有原子
当写一个volatile变量时,JMM会把该线程工作内存中对应的共享变量刷新到主内存中。
当读一个volatile变量时,JMM会把该线程工作内存中对应的共享变量副本设置成无效。线程直接从主内存中读取共享变量
因此volatile读/写可以实现线程间的通信
前面提到voliatile可以禁止指令重排序和保证可见性,为了保证voliatile内存语义,JMM会分别限制编译器和处理器重排序。这种限制主要是通过内存屏障来实现的。
是否能重排序 | 第二个操作 | ||
---|---|---|---|
第一个操作 | 普通读/写 | voliatile读 | voliatile写 |
普通读/写 | 否 | ||
voliatile读 | 否 | 否 | 否 |
voliatile写 | 否 | 否 |
总结
第一个操作是voliatile读的时候,不管第二个操作是什么都不能重排序
第二个操作是voliatile写的时候,不管第一个操作是什么都不能重排序
第一个操作是voliatile写的时候,第二个操作是voliatile读的时候不能重排序
为了实现voliatile的内存语义,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序:
(1)在每个volatile写操作前面插入一个StoreStore屏障
(2)在每个volatile写操作后面插入一个StoreLoad屏障
(3)在每个volatile读操作后面插入一个LoadLoad屏障
(4)在每个volatile读操作后面插入一个LoadStore屏障
锁是Java并发编程中最重要的同步机制。锁除了可以让临界区互斥执行,还可以让释放锁的线程向获取同一个锁的线程发送消息
当线程释放锁时,JMM会把线程中的工作内存对应的共享变量刷新到主内存中
当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被监视器保护的临界区资源必须从主内存中读取共享变量
因此锁的释放/读取可以实现线程间的通信