先说几个概念
可见性:一个线程对共享变量值的修改,能够及时的被其他线程看到
共享变量:如果一个变量在多个现成的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量。
所有的变量都存在主内存中,每个线程都有自己的工作内存
线程对变量的所有操作只能在自己的工作内存中进行,不能直接与主内存读写
线程之间的变量传递需要通过主内存来完成
可见性要求的两点:
1.线程修改后的共享变量,能够及时的从工作内存中刷新到主内存中
2.其他线程能够及时的把共享变量的最新值从主内存更新到自己的工作内存中
synchronized
1.原子性(同步)
2.可见性
关于synchronized的两条规定:
1.线程解锁前,必须把共享变量的最新值刷新到主内存中
2.加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中读取最新的值,
从而可以保证可见性。
指令重排序:
编译器或者处理器为了提高性能做的一些优化,对于一些代码,翻译成机器码之后,重排序可能更利于运行的性能,主要有三种:
1.编译器优化的重排序。在保证结果正确的情况下,可以重新安排代码的执行顺序
2.指令级并行重排序。处理器的优化
3.内存系统的重排序。处理器对读取缓存的优化
as-if-serial:
无论怎么重排序,程序运行的结果,都应该和代码顺序执行的结果一致(Java编译器,运行时和处理器都会保证在
单线程下遵循as-if-serial语义)
但是在多线程中,重排序可能会造成内存可见性的问题。
volatile
只能保证可见性,但是不能保证原子性
可见性:通过加入内存屏障和禁止重排序优化来实现的
-对于volatile变量执行写操作时,会在写操作后加入一条store屏障指令命令(把共存内存强制写到主缓存中)
-对于volatile变量执行读操作时,会在读操作前加入一条load屏障指令命令(强制缓存中失效)
不能保证原子性,比如num++;
对比
volatile不需要加锁,更轻量级,不会阻塞线程,效率高,如果变量的写操作不依赖于上一次的操作,比如boolean变量,价格的变化等,可以采用这种
从内存可见性角度来说,volatile读相当于加锁,volatile写相当于解锁
volatile只能保证可见性,无法保证原子性