原子性:
一个或多个指令在CPU执行过程中不允许中断的。
之前文章,在汇编层面证明,i++不是原子操作:
https://blog.csdn.net/weixin_41905047/article/details/129542913
可以通过加锁实现操作的原子性,java中加锁有两种形式,通过synchronized关键字,lock锁。
有序性:
概念:指令在CPU调度执行时,CPU会为了提升执行效率,在不影响结果的前提下(满足happens-before原则),对CPU指令进行重新排序。
解决方案:如何避免指令重排,对应的属性用volatile修饰,就不会进行指令重排。
使用场景:单例模式DCL双重判断
public class Single {
private volatile static Single single;
private Single() {
}
public static Single getInstance() {
if (null == single) {
synchronized (Single.class) {
/**
* 加volatile的原因,避免下面的情况:
* 创建对象会经历:申请内存,初始化,关联是正常顺序,如果CPU对指令重排,可能会造成
* 申请内存,关联,初始化,在还没有初始化时,其他线程来获取数据,
* 导致获取到的数据虽然有地址引用,但是内部的数据还没初始化,都是默认值,
* 导致使用时,可能出现与预期不符的结果
*/
if (null == single) {
single = new Single();
}
}
}
return single;
}
}
可见性:
概念:
CPU在处理时,需要将主内存数据同步到寄存机中再执行指令,执行完指令后,需要将寄存器数据扔回到主内存中,但是寄存器数据同步到主内存是遵循MESI协议的,换句话说,不是每次操作结束就将CPU缓存数据同步到主内存,造成多个线程看到的数据不一样。
解决方案:
使用volatile或者synchronized,操作后都会同步数据到主内存。