原子性,有序性,可见性
synchronized 具有原子性,有序性,可见性;
volatile 具有有序性,可见性
原子性
原子性是指在一个操作过程中,要不就全部执行成功,要不就全部执行失败。即使在多个线程一起执行的时候,只要操作开始,就不会被其他线程干扰。
最常见的例子:i++
i++ 并不是原子操作,因为i++ 实际上包含了3个操作,1. 读取变量i的值;2:对i进行加一的操作;3.将计算后的值再赋值给变量i,这三个操作无法构成原子操作。
java内存模型中定义了8中操作都是原子的,不可再分的
- lock(锁定):作用于主内存中的变量,它把一个变量标识为一个线程独占的状态;
- unlock(解锁):作用于主内存中的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
- read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便后面的load动作使用;
- load(载入):作用于工作内存中的变量,它把read操作从主内存中得到的变量值放入工作内存中的变量副本
- use(使用):作用于工作内存中的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作;
- assign(赋值):作用于工作内存中的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作;
- store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送给主内存中以便随后的write操作使用;
- write(操作):作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。
有序性
有序性:即程序执行的顺序按照代码的先后顺序执行。
Java内存模型中的程序天然有序性可以总结为一句话:如果在本线程内观察,所有操作都是有序的;如果在一个线程中观察另一个线程,所有操作都是无序的。前半句是指“线程内表现为串行语义”,后半句是指“指令重排序”现象和“工作内存主主内存同步延迟”现象。
有序性的语意有几层:
1. 最常见的就是保证多线程运行的串行顺序
2. 防止重排序引起的问题
例如:单例(双重检验锁定(Double-checked Locking))
if(instance==null){
synchronized (Singleton.class){
if(instance==null){
instance = new Singleton();
}
}
}
这条语句实际上包含了三个操作:1.分配对象的内存空间;2.初始化对象;3.设置instance指向刚分配的内存地址。
由于存在重排序的问题,2和3有可能调转。假如调转的话就有可能在线程A中进行instance = new Singleton()操作,因为重排序的问题先执行3后执行2,那么在线程B执行第一个instance==null的时候,线程A执行先执行了3,2还没执行,导致返回的还是空。
3. 程序运行的先后顺序。比方JMM定义的一些Happens-before规则
可见性
可见性是指:当一个线程修改了线程共享变量的值,其它线程能够立即得知这个修改。
Java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介的方法来实现可见性的。
通过之前对synchronzed内存语义进行了分析,当线程获取锁时会从主内存中获取共享变量的最新值,释放锁的时候会将共享变量同步到主内存中。从而,synchronized具有可见性。同样的在volatile分析中,会通过在指令中添加lock指令,以实现内存可见性。
参考文章:
https://blog.csdn.net/qq_33689414/article/details/73527438
https://www.jianshu.com/p/cf57726e77f2(这位作者的文章都很不错)