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操作,但好處是得到的對象值是主存中最新的。

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