缓存一致性问题
- 起因:高速缓存
- 方案
- 总线LOCK#锁,释放锁前该块内存无法被别的cpu或线程访问
- 缓存一致性协议(Intel的MESI协议),cpu写共享变量时,会通知其它cpu将缓存失效
并发性质
- 原子性:事务、原语
- 可见性:值在高速缓存中被改变,未来得及写入内存,别的线程去读就错了
- 有序性(JVM指令重排)
语句1和2被重排,下面的多线程可能就挂了
// 线程1 - 负责加载context,然后发通知
context = loadContext(); // 语句1
inited = true; // 语句2
//线程2 - 监听inited通知信号,业务处理
while(!inited) {
sleep();
}
doSomethingWithConfig(context);
JMM Java内存模型
- 线程先将堆中对象属性拷到工作内存,修改,然后刷回堆
- 原子性:synchronized/Lock
- 可见性
- volatile:读值时强制从主内存读;值改变后会马上刷入主内存
- synchronized/Lock:出同步前刷入主内存
- 有序性
- volatile
- 读写volatile变量时,前面操作已完成,后面操作未开始
- 语句重排时无法突破volatile边界
- 上面例子中,给inited属性加volatile即可
- happens-before(先行发生)原则
- 程序次序:单线程内,先写的代码先执行(这个是从结果角度出发,和指令重排不矛盾)
- 锁定规则:同一个锁,必先unlock,再lock
- volatile变量规则:一个线程先写,一个线程后读,那么必先写完
- 传递规则:A先于B,B先于C,则A先于C
- 线程启动:Thread的start方法是该线程的第一个动作
- 线程中断:interrupt方法的调用先于被中断线程检测到中断信号
- 线程终结:所有操作先于终止检测
- 对象终结:对象初始化的结束,早于finalize方法的开始
实现机制
- volatile会在汇编代码中增加一个lock前缀指令(相当于内存屏障)
- 确保指令重排时不会把其后面的指令排到其之前,也不会把前面的指令排到内存屏障后面;执行到内存屏障指令时,前面操作均已完成
- 强制将对缓存的修改立即写入主内存
- 写操作会导致其他CPU中对应缓存无效
举例场景
// 只有volatile
public class Test {
private volatile int inc = 0;
public void increase() {
inc++; // 线程1和2同时读入inc,线程2快速+1写回主内存;由于线程1已读过值,直接开加,所以错了
}
public static void main(String[] args) {
final Test test = new Test();
for(int i = 0; i < 10; i++) {
new Thread(() -> {
for(int j = 0; j < 1000; j++) {
test.increase();
}
}).start();
}
while(Thread.activeCount() > 1) {
Thread.yield();
}
System.out.println(test.inc);
}
}
// synchronized,阻塞了从主内存取值的动作
private int inc = 0;
public synchronized void increase() {
inc++;
}
// Lock
private int inc = 0;
Lock lock = new ReentrantLock();
public void increase() {
try {
lock.lock();
inc++;
} finally {
lock.unlock();
}
}
// AtomicInteger
private AtomicInteger inc = new AtomicInteger();
public void increase() {
inc.getAndIncrement();
}
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
- AtomicInteger -> Unsafe.CAS -> 处理器CMPXCHG指令(原子指令)