JMM描述了Java多線程對共享變量的訪問規(guī)則,以及在JVM中將變量存儲到內(nèi)存和從內(nèi)存中讀取變量這樣的底層細(xì)節(jié)。

java內(nèi)存模型如上圖所示,每個(gè)線程都有自己獨(dú)立的工作內(nèi)存,當(dāng)線程要訪問內(nèi)存中的變量時(shí),會先將內(nèi)存中的變量值復(fù)制到自己的工作內(nèi)存,然后再訪問;當(dāng)線程要改變內(nèi)存中的變量值時(shí),也是先改變自己工作內(nèi)存中副本的變量值,然后再刷新到內(nèi)存中。當(dāng)線程一改變了某個(gè)變量的值,而線程二想要訪問該值時(shí),可能會存在以下情況,即線程一的改變還沒刷到內(nèi)存,或者線程二里面緩存了老值,沒有去內(nèi)存中拿最新的值,這時(shí)就相當(dāng)于線程一的改變對線程二不可見了。
Java通過以下幾種方式來保證變量在線程之間的可見性:
synchronized 當(dāng)線程調(diào)用synchronized修飾的方法(代碼塊)時(shí),會清空自己工作內(nèi)存中所有的共享變量,并從內(nèi)存中重新讀取,這樣就保證了可以看到別的線程做的改動;當(dāng)退出synchronzied函數(shù)時(shí),會將工作內(nèi)存中所有更新過的共享變量的值回寫到內(nèi)存中,保證其他線程可以讀到該線程更改的值。
volatile volatile變量也能保證可見性,每次對volatile的讀操作都會導(dǎo)致工作內(nèi)存中的變量被內(nèi)存中的最新值覆蓋,對volatile的寫操作,也會馬上更新到主內(nèi)存中去,這樣就保證了每個(gè)線程對變量的改變對其他線程都是可見的。
synchronized 和volatile的不同:
既然synchronized可以實(shí)現(xiàn)可見性了為啥還引入volatile呢?兩者雖然都能實(shí)現(xiàn)可見性,還是有不同之處的,synchronized需要對程序加鎖,比較耗費(fèi)資源;synchronized修飾的代碼塊,在一個(gè)線程退出去之前,其他線程是不能訪問的,這樣就提供了對某地代碼的原子性操作。volatile則比較輕便,不需要加鎖,但是不能保證操作的原子性,像i++,這種操作,它是無法保證結(jié)果正確的。final final修飾的變量不會在構(gòu)造函數(shù)返回前被訪問到,這樣就可以保證final變量的不可變性。因?yàn)槿绻驗(yàn)橹噶钪嘏判?,其他線程在final對象還沒初始化完之前拿到了該變量的引用,有可能讀到一個(gè)初始化之前的值,然后后面再讀又讀到一個(gè)初始化之后的值,造成的現(xiàn)象就是final修飾的變量值可變了。
cocurrent包 java concurrent jar包提供了大量同步代碼塊的工具,方便我們正確的同步各個(gè)線程的執(zhí)行。