0
点赞
收藏
分享

微信扫一扫

多线程并发情况下的数据一致性问题


前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。​​点击跳转到网站​​

首先,有一个静态的布尔变量:

public static boolean  flag = false;

然后,开启两个线程。

A线程:

new Thread(() -> {

System.out.println(Thread.currentThread() + "start...");

while(!flag) {

}

System.out.println(Thread.currentThread() + "success...");

}).start() ;

很明显,这是一个死循环。

B线程:

//延迟1秒,确保上面的先执行Thread.sleep(1000);

new Thread(() -> {

flag = true;

System.out.println(Thread.currentThread() + "flag 变为 true了!");

}).start() ;

B线程在1秒后执行,把flag变为true。那么按理说,因为flag是静态属性,它的值发生变化后,A线程应该感应到这种变化,从而跳出死循环。

可是实际上:

Thread[Thread-0,5,main]start...
Thread[Thread-1,5,main]flag 变为 true了!

A线程一直在死循环,并没有发生变化。

为啥会和想的不同?

多线程并发情况下的数据一致性问题_实例化

如图,JMM内存模型中,每一个线程都有一个高速缓冲区,这个区域是每个线程自己独有的内存空间,flag其实在主内存中,每个线程是拷贝了一个flag副本到自己的内存。

因为A线程有死循环,一直在占用着flag副本,也就没有发现该变量已经被其他线程修改了。

解决办法--用volatile关键字

public static volatile boolean  flag = false;

当flag被volatile关键字修饰,它的值发生变化时,JVM会立刻将flag重新写入主内存。并且通知其他线程(引用了这个副本的线程)立刻销毁flag变量。

从而迫使这些线程重新从主内存读取flag,这样就可以做到保证多线程下变量的数据一致性了。

实际应用--双重检测锁机制下的对象半实例化问题解决

public class MySingleton {

private static volatile MySingleton mySingleton;

private MySingleton() {}

public static MySingleton newInstance() {
if(mySingleton == null) {
synchronized (mySingleton) {
if(mySingleton == null) {
mySingleton = new MySingleton();
}
}
}

return mySingleton;
}
}

为什么会出现半实例化?

mySingleton = new MySingleton();

这行代码在汇编里面执行的情况下大致是这样的,伪代码如下:

1.mySingleton 分配内存

2.new mySingleton() 实例化

3.mySingleton实例对象赋值给mySingleton 

其中,步骤2 和 步骤3 即便颠倒顺序也不会影响最终的结果,所以CPU可能会进行指令重排序。如果步骤3先执行了,而恰好在这个时候别的线程检测到mySingleton不是null,就直接返回了。此时,mySingleton甚至还没有调用构造方法呢,只是一个空的引用(但已经不是null了)。

所以,这就导致了对象半实例化。解决方法也是加上volatile关键字。

举报

相关推荐

0 条评论