java中volatile关键字的作用和原理

在之前的一篇博客中挖过坑,今天来填一下。之前挖坑的博客

在那篇博客中,我提到了volatile关键字,它的作用比较和synchronized类似,但又区别很大。。。要理解它就得要从java中的内存管理讲起了。

在java虚拟机的内存模型中,分为主内存和线程内存。在这里我们分别将其简称为主存线存。然后java虚拟机在把类加载为对象的时候,这个对象就被存放在主存中。当有一个线程X想要访问(或修改)这个对象A时,它并不能直接对主存中的对象A进行操作,而是要把对象A“复制”一份到线程X的线存中去,然后访问(或修改)完之后,将其覆盖至主存。

这就是java中线程操作时的内存模型,按照这个模型,我们可以想象一下有多个线程同时访问复制主存中的对象,然后再修改、覆盖至主存。是不是就会发生我们常说的线程不安全?那么如何解决这个问题呢?

首先我们来假设一种场景---过马路。过马路的规则非常简单,如果路人遇到了绿灯就走,遇到了红灯就停下。那么在java中这个过马路的模型要如何实现?可能又小伙伴会说,new 一个红绿灯对象,然后new 多个路人线程去读红绿灯的值,不就搞定了吗?可是事情并不简单呀。。。

如果按照这个简单的模型,当红绿灯信号发生变化时,许多路人线程中的红绿灯副本并不能及时刷新,从而导致路人线程一直在走。这个时候就要使用到volatile关键字了。为了防止路人线程中的红绿灯副本刷新不及时,那就干脆每次读取红绿灯的时候,都从主存中直接读嘛。也就是说被volatile修饰的对象,不会被“复制”线存中,而是当每次需要用到这个对象的时候,从主存中直接读取。这一做法的好处就是,每一个路人线程都能及时地获取到最新的红绿灯信息,但缺点也很明显,每一次直接访问主存都会比“刷副本”慢一些。

总结一下:java虚拟机中所有的对象都是被加载于主存中,如果线程想要访问或修改这些对象,有两种方法,第一,将对象“复制”(load)一份副本保存至线程自身的线存中去,当使用完成之后(线程结束之后),线存中的“副本”会覆盖(save)主存中的原对象。要注意的是线程中无论对“副本”进行何种操作,都不会影响到主存中的原对象,只有当线程执行完毕之后,才会把线存中的“副本”覆盖至主存。第二种方法,用volatile关键字对对象进行修饰,此时对象不会在线程一开始“复制”(load)至线存,当线程需要用到该对象时,才会从主存中“复制”(load)原对象,修改完成后(此时线程并不一定执行完毕),直接覆盖(sava)主存当中的对象。两种方式的区别在于,被volatile修饰的对象会增加线程运行时的load/save操作,但好处是得到的对象值是主存中最新的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章