1. JAVA内存模型 JMM
JMM体现在在以下几方面:
原子性:指令不会受到线程上下文切换的影响
可见性:保证指令不会受CPU缓存的影响
有序性:指令不会受CPU指令并行优化的影响
1.1 可见性
因为t要频繁的从主存中读取run的值,JIT即时编译器会将run的值缓存到自己的高速缓存中,减少主存对run的访问,提高效率。
解决方法:
(1)volatile关键字:修饰成员变量和静态成员变量,可以强制从主存总读取。适用于一个线程写,剩下线程读。不保证原子性
(2)synchronized关键字:既能保证原子性也能保证可见性,但是成本高
1.2有序性
JVM会对指令进行指令重排来提高效率:在不改变结果的前提下,指令通过重排序和组合实现指令级并行
解决方法:volatile
例子:double-checked locking 懒汉单例模式
public final class Singleton{
private static volatile Singleton INSTANCE = null;
private Singleton(){}
public static Singleton getInstance(){
if(INSTANCE == null){
synchronized(Singleton.class){
if(INSTANCE == null)
INSTANCE = new Singleton();
}
}
return INSTANCE;
}
}
如果不加volatile,可能会引起指令重排:线程1进入同步代码块刚刚执行完new Singleton(),对应的字节码为下面四条
指令21(调用构造方法new Singleton(),申请空间)和24(赋值,把构造好的那块地址赋值给INSTANCE)可能会重排。
1.3 volatile原理
底层实现原理是内存屏障, Memory Barrier
对volatile读指令前加入读屏障,对volatile写指令后会加入写屏障。读写屏障保证有序性和可见性
eg.
保证有序性:写屏障之前的代码不会被重排到写屏障后,读屏障之后的代码不会重排到屏障之前
保证可见性:num=2和 ready = true会先同步到主存中,而if(ready)之后的代码都会从主存中读取。
2.CAS
compare and swap 无锁并发
原子整数:AtomicInteger,
原子引用:AtomicReference(存在ABA问题),AtomicStampedReference, AtomicMarkableReference
原子数组:
原子累加器:LongAdder(效率更高,原理等效于MAPREDUCE)
LongAdder原理:缓存一致性,防止伪共享
3. Unsafe对象
由于是底层,不能直接获得,需要通过反射获得