線程間通信與同步
在命令式編程中,線程之間的通信機(jī)制有兩種:共享內(nèi)存和消息傳遞。在共享內(nèi)存的并發(fā)模型里,線程之間共享程序的公共狀態(tài),通過(guò)寫-讀內(nèi)存中的公共狀態(tài)進(jìn)行隱式進(jìn)行通信。在消息傳遞的并發(fā)模型里,線程之間沒(méi)有公共狀態(tài),而是必須通過(guò)發(fā)送消息來(lái)顯示進(jìn)行通信。
同步是指程序中用于控制不同線程間操作發(fā)生相對(duì)順序的機(jī)制。在共享內(nèi)存并發(fā)模型中,同步是顯示進(jìn)行的。在消息傳遞的并發(fā)模型中,同步是隱式進(jìn)行的。Java采用的是共享內(nèi)存并發(fā)模型,線程之間的通信是隱式進(jìn)行的,整個(gè)過(guò)程對(duì)程序員完全透明。
內(nèi)存模型的抽象結(jié)構(gòu)
實(shí)例域、靜態(tài)域和數(shù)組元素都存儲(chǔ)在堆內(nèi)存中,堆內(nèi)存在線程之間共享。局部變量、方法定義參數(shù)和異常處理器參數(shù)不會(huì)在線程之間共享,這些變量不存在內(nèi)存可見(jiàn)性問(wèn)題,也不受內(nèi)存模型的影響。
Java內(nèi)存模型(JMM)控制Java線程之間的通信,JMM決定一個(gè)線程對(duì)共享變量的寫入何時(shí)對(duì)另一個(gè)線程可見(jiàn)。從抽象的角度來(lái)看,JMM定義了線程和主內(nèi)存之間的抽象關(guān)系:線程之間的共享變量存儲(chǔ)在主內(nèi)存中,每個(gè)線程都有一個(gè)私有的本地內(nèi)存,本地內(nèi)存中存儲(chǔ)了該線程以讀/寫共享變量的副本。本地內(nèi)存是JMM的一個(gè)抽象概念,本身并不是真實(shí)存在。
指令序列重排序
在執(zhí)行程序時(shí),為了提高性能,編譯器和處理器常常會(huì)對(duì)指令做重排序。重排序分為以下3中類型:
- 編譯器優(yōu)化的重排序。編譯器在不改變單線程程序語(yǔ)義的前提下,可以重新安排語(yǔ)句的執(zhí)行順序。
- 指令級(jí)并行的重排序。現(xiàn)代處理器采用了指令級(jí)并行技術(shù)來(lái)將多條指令重疊執(zhí)行。如果不存在數(shù)據(jù)依賴性,處理器可以改變語(yǔ)句對(duì)應(yīng)機(jī)器指令的執(zhí)行順序。
- 內(nèi)存系統(tǒng)的重排序。由于處理器使用緩存和讀/寫緩沖區(qū),這使得加載和存儲(chǔ)操作看上去可能是在亂序執(zhí)行。
重排序可能會(huì)導(dǎo)致多線程程序出現(xiàn)內(nèi)存可見(jiàn)性問(wèn)題。對(duì)于編譯器,JMM的編譯器重排序規(guī)則會(huì)禁止特定類型的編譯器重排序。對(duì)于處理器重排序,JMM的處理器重排序規(guī)則會(huì)要求Java編譯器在生成指令序列時(shí),插入特定類型的內(nèi)存屏障指令,通過(guò)內(nèi)存屏障指令來(lái)禁止特定類型的處理器重排序。JMM屬于語(yǔ)言級(jí)的內(nèi)存模型,它確保在不同的編譯器和不同的處理器平臺(tái)之上,通過(guò)禁止特定類型的編譯器重排序和處理器重排序,為程序員提供一致的內(nèi)存可見(jiàn)性保證。