首页 > 经验记录 > java > 从JMM的角度浅析volatile关键字功能及作用以及其基础原理

从JMM的角度浅析volatile关键字功能及作用以及其基础原理

看下这样一段代码:

我在一个方法中建立了个死循环,循环的判断条件为一个boolean类型的成员变量。

然后在main线程中,创建了一个新的名为 “t1” 的线程,去执行这个方法。

等待一秒后,mian线程自身,将该成员变量的值改为false,试图使其不满足条件从而循环终止。

按照正常的逻辑来说,按照脑海中预演的情况来说,应该是没问题的。

可是执行后却没有得到想要的结果,控制台输出 “start” 后再无反应,程序一直没有终止,即 死循环没有退出。

 

想要让程序正确执行的话,将定义语句 boolean flag = true; 改为 volatile boolean flag = true;  程序就可以正确执行,这是为什么呢?

 

这里要涉及一点 java 内存模型,JMM。

JMM 里头,他有分配一块内存,叫主内存。类比于操作系统中的主存,主要存放线程间的共享数据(除去方法参数、局部变量)。

然后,每个线程在执行的过程当中,都有一个WorkingMemory,是主内存中部分数据的副本,只能该线程访问,类比于硬件的高速缓存。线程对数据的访问通常只能在workingMemory中进行,不能直接与主存交互。

比如cpu运行多个不同的线程的话,每个线程都有一个WorkingMemory,cup就是将主内存里边的数据读过来读到WorkingMemory里,然后再进行修改,比如+1+1+1+1,修改完后,再给他写回内存去。

 

我上边写的代码,这几个线程是怎么执行的呢?

在我的代码中,flag 是存在于堆内存中的 v 对象中。

当线程 t1 开始跑后,它首先会从主内存中把这个 flag 变量给它 copy 到自己的工作内存里边,然后开始运行。在cpu处理的过程之中,由于这cpu处理这个线程的部分他非常的忙,一直在while循环嘛,就不再去读主内存里边的数据了,一直在读自己的缓冲区(线程的工作内存)。

main 线程,改flag的参数,也是把 flag 参数 true 给读到自己的工作内存里边,然后进行修改,修改完事后,发现自己工作内存中的数据和主存中不一致,于是将自己工作内存中的数据给他刷新到主存中去。

这个时候就有问题了。第一个线程没有在主存里重新读啊?所以,这时候第一个线程就结束不了。

 

 

volatile 关键字功能及作用:

简单的来说,volatile 关键字,主要功能是使一个变量在多个线程间可见。

volatile关键字修饰的变量一旦在主存中被改变时,就会通知别的使用到该变量的线程:你们的缓冲区中的内容过期了,需要再重新刷新一下。

以上面那个代码例子来说 ,这样定义语句 volatile boolean flag = true;

当 main 线程修改主内存中 flag 的值时。这个时候, t1 线程,就会去重新读一遍主存,刷新自己的缓冲区(工作空间)。此时 t1 线程中的 flag 刷新为 false ,故循环停止。程序运行完毕。

 

虽然 volatile 用起来比较简单,但是该关键字背后代表的逻辑还是很深的。

 

 

还有个点要注意:

volatile并不能保证多个线程共同修改某一变量时所带来的不一致问题,也就是说 volatile 不能代替 synchronized。

当然, volatile 的效率高 synchronized 很多倍。但是该上锁的时候也只能上锁。

synchronized 是 既有可见性,又保证原子性。而 volatile 只保证可见性。synchronized的实现也是比 volatile 更重的。

 

 

关于 synchronized  本来也有一篇文章要写的。想想还是算了,本来想写点加锁释放锁的原理、可重入性质和可见性和原理啥的。

不过我过了归纳的那一段时间了,现在想归纳总结一通怪麻烦的。反正这些知识脑子里都有,就不记录了。

 

           


EA PLAYER &

历史记录 [ 注意:部分数据仅限于当前浏览器 ]清空

      00:00/00:00