1.Volatile特性
- 可见性-对于一个Volatile变量的读,总能看得到任意线程对这个Volatil最后的写入
- 原子性,对于任意单个Volatile的读写具有原子性,类型Volatil++复合操作不具有原子性
2.Volatile保证内存可见性
当写一个Volatiel变量时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中,并会让其他读该Volatiel变量线程的本地内存无效,去主内存读取。
- 线程A写一个Volatiel变量时,实际上是A线程向接下将要读取这个Volatiel变量的某个线程发出了消息(共享变量的修改)
- 线程B读取一个Volatiel变量,实际上是B线程接收之前某个线程发出的消息
- 线程A写Volatiel变量,线程B读Volatiel变量,这个过程实际上就是线程A通过主内存向线程B发送消息
3.Volatile内存屏障
我们在使用Volatile时会插入内存屏障来做禁止重排,分别有下面4种
- 在每个Volatile写操作的前面插入一个StoreStroe屏障
- 在每个Volatile写操作的后面插入一个StoreLoad屏障
- 在每个Volatile读操作的后面插入一个LoadLoad屏障
- 在每个Volatile读操作的后面插入一个LoadStore屏障
StoreStroe屏障的意思是说,禁止Volatile写操作上面的写操作与当前Volatile写操作重排序
StoreLoad屏障的意思是说,禁止Volatile写操作与下面的Volatile读写操作重排序
LoadLoad屏障的意思是说,禁止Volatile读操作下面所有普通读与Volatile读操作重排
LoadStore屏障的意思是说,禁止Volatile读操作下面所有普通写与Volatile写操作重排
4.锁
锁是Java重要的同步机制,锁除了让临界互斥执行外,还可以让释放锁与获取锁的线程通信,锁里面不会禁止线程临界区重排优化,因为互斥,其他阻塞线程观察不到当前线程中的重排。
锁的底层是由同步器实现的(AQS),同步器底层依靠CAS、Volatile变量。
5.锁的内存可见性保证
当线程释放锁时,JMM会把该线程对应的本地内存共享比哪里刷新到主内存中,当某个线程获取锁时JMM会把这个线程对应的本地内存设置为无效,去主内存读取。
- 线程A释放锁时,实际上是A线程向接下将要读取这个锁的某个线程发出了消息(共享变量的修改)
- 线程B获取到锁,实际上是B线程接收之前某个线程发出的消息
- 线程A释放锁,线程B获取锁,这个过程实际上就是线程A通过主内存向线程B发送消息
6.对比Volatile与锁
对比Volatile与锁的内存可见性可以发现,锁的释放与Volatile的写一样,锁的获取与Volatile的读一样,Volati的读写与锁的释放获取有相同的效果
参考:
《Java并发编程的艺术》