1700450269
图9-1 线程读取变量的示意图
1700450270
1700450271
从示意图上我们可以看到,线程在初始化时从主内存中加载所需的变量值到工作内存中,然后在线程运行时,如果是读取,则直接从工作内存中读取,若是写入则先写到工作内存中,之后再刷新到主存中,这是JVM的一个简单的内存模型,但是这样的结构在多线程的情况下有可能会出现问题,比如:A线程修改变量的值,也刷新到了主存中,但B、C线程在此时间内读取的还是本线程的工作内存,也就是说它们读取的不是最“新鲜”的值,此时就出现了不同线程持有的公共资源不同步的情况。
1700450272
1700450273
对于此类问题有很多解决办法,比如使用synchronized同步代码块,或者使用Lock锁来解决该问题,不过,Java可以使用volatile更简单地解决此类问题,比如在一个变量前加上volatile关键字,可以确保每个线程对本地变量的访问和修改都是直接与主内存交互的,而不是与本线程的工作内存交互的,保证每个线程都能获得最“新鲜”的变量值,其示意图如图9-2所示。
1700450274
1700450275
1700450276
1700450277
1700450278
图9-2 volatile变量操作示意图
1700450279
1700450280
明白了volatile变量的原理,那我们思考一下:volatile变量是否能够保证数据的同步性呢?两个线程同时修改一个volatile是否会产生脏数据呢?我们来看下面的代码:
1700450281
1700450282
class UnsafeThread implements Runnable{
1700450283
1700450284
//共享资源
1700450285
1700450286
private volatile int count=0;
1700450287
1700450288
@Override
1700450289
1700450290
public void run(){
1700450291
1700450292
//增加CPU的繁忙程度,不用关心其逻辑含义
1700450293
1700450294
for(int i=0;i<1000;i++){
1700450295
1700450296
Math.hypot(Math.pow(92456789,i),Math.cos(i));
1700450297
1700450298
}
1700450299
1700450300
//自增运算
1700450301
1700450302
count++;
1700450303
1700450304
}
1700450305
1700450306
public int getCount(){
1700450307
1700450308
return count;
1700450309
1700450310
}
1700450311
1700450312
}
1700450313
1700450314
上面的代码定义了一个多线程类,run方法的主要逻辑是共享资源count的自加运算,而且我们还为count变量加上了volatile关键字,确保是从主内存中读取和写入的,如果有多个线程运行,也就是多个线程执行count变量的自加动作,count变量会产生脏数据吗?想想看,我们已经为count加上了volatile关键字呀!模拟多线程的代码如下:
1700450315
1700450316
public static void main(String[]args)throws Exception{
1700450317
1700450318
//理想值,并作为最大循环次数
[
上一页 ]
[ :1.700450269e+09 ]
[
下一页 ]