1、先行发生原则happens-before
- happens-before体现的是对可见性和有序性的约束。
- happens-before是并发环境下,两个操作是否可能存在冲突的判断依据
y一定等于5?
如果线程A的操作(x=5)先行发生于线程B的操作(y=x),或者说这两个事件存在先行发生原则,那y=5一定成立,反之则不一定,因为x=5的改变可能还没从A线程的工作内存刷回主内存,线程就暂时挂起了。
但如果Java内存模型中的有序性都得靠volatile和synchronized来实现,就非常繁琐,而且日常开发也没见处处加这些关键字,这是因为谁先谁后在先行发生原则里已经立好了规矩。
2、happens-before总原则
举例:
值日表里写了周一张三,周二李四,但现在张三周一临时有事,和李四换班后,教室还是能打扫干净
3、8条happens-before规则
直白讲就是:同一个线程,前面一个操作把变量x赋值为1,那后面一个操作肯定知道x已经变成1了
直白说就是:一定是:A线程unlock后,B线程才能对同一个锁lock
Lock lock = new ReentrantLock();
lock.lock();
try{
}finally{
//先
lock.unlock();
}
//后
lock.lock();
try{
}finally{
lock.unlock();
}
执行顺序一定是第4行先,第2行后:
Thread t1 = new Thread(() -> {
System.out.println("QWE"); //后
},"t1");
t1.start(); //先
直白说:检测到中断事件发生(检测到中断标志位变了),是先发生了interrupt方法的调用。一定是先发烧了,温度计才能检测到体温变了。
直白说:线程中的操作(比如run方法体)先全部执行完,线程才终止
翻译:肯定是先new了一个对象,才能垃圾回收这个对象
4、案例
private int value = 0;
private int getValue(){
return value;
}
private int setValue(){
return ++value;
}
现在有线程A和线程B,线程A(在时间上先)调用了setValue方法,然后线程B调用同一对象的getValue方法,那线程B的返回值是?
对照上面的8条规则:
- 两个方法在不同线程,第一条规则用不上
- 两个方法都未加锁,规则2也pass
- 共享变量value没有加volatile,规则3pass
- 先行发生规则不等价于时间上的先,这里目前直接没有已知的先行发生规则,无从传递,pass
- …
⇒ 无法通过happens-before原则推导出线程A happens-before线程B,虽然可以确认在时间上线程A优先于线程B,无法确认线程B获得的结果是什么,所以这段代码不是线程安全的。
怎么修复?
方式一:加synchronized,如下,这样性能损失太大
private int value = 0;
private synchronized int getValue(){
return value;
}
private synchronized int setValue(){
return ++value;
}
方式二:把value定义为volatile变量,由于setter方法对value的修改不依赖value的原值,满足volatile关键字使用场景
private volatile int value = 0;
private int getValue(){
return value; //利用volatile保证读取操作的可见性
}
private synchronized int setValue(){ //利用synchronized保证复合操作的原子性
return ++value;
}
利用volatile保证读取操作的可见性,利用synchronized保证复合操作的原子性,结合使用锁和volatile 变量来减少同步的开销