1700450260
编写高质量代码:改善Java程序的151个建议 建议123:volatile不能保证数据同步
1700450261
1700450262
volatile关键字比较少用,原因无外乎两点,一是在Java 1.5之前该关键字在不同的操作系统上有不同的表现,所带来的问题就是移植性较差;二是比较难设计,而且误用较多,这也导致它的“名誉”受损。
1700450263
1700450264
我们知道,每个线程都运行在栈内存中,每个线程都有自己的工作内存(Working Memory,比如寄存器Register、高速缓冲存储器Cache等),线程的计算一般是通过工作内存进行交互的,其示意图如图9-1所示。
1700450265
1700450266
1700450267
1700450268
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;
[
上一页 ]
[ :1.700450259e+09 ]
[
下一页 ]