鍍金池/ 教程/ Java/ 競(jìng)態(tài)條件與臨界區(qū)
Slipped Conditions
阻塞隊(duì)列
無(wú)阻塞算法
嵌套管程鎖死
Java 并發(fā)性和多線程介紹
死鎖
線程安全及不可變性
并發(fā)編程模型
Java 中的讀/寫(xiě)鎖
剖析同步器
競(jìng)態(tài)條件與臨界區(qū)
多線程的優(yōu)點(diǎn)
CAS
線程通信
如何創(chuàng)建并運(yùn)行 java 線程
阿姆達(dá)爾定律
避免死鎖
信號(hào)量
多線程的代價(jià)
饑餓和公平
線程池
重入鎖死
Java 中的鎖
Java 內(nèi)存模型
線程安全與共享資源
Java 同步塊

競(jìng)態(tài)條件與臨界區(qū)

在同一程序中運(yùn)行多個(gè)線程本身不會(huì)導(dǎo)致問(wèn)題,問(wèn)題在于多個(gè)線程訪問(wèn)了相同的資源。如,同一內(nèi)存區(qū)(變量,數(shù)組,或?qū)ο螅?、系統(tǒng)(數(shù)據(jù)庫(kù),web services 等)或文件。實(shí)際上,這些問(wèn)題只有在一或多個(gè)線程向這些資源做了寫(xiě)操作時(shí)才有可能發(fā)生,只要資源沒(méi)有發(fā)生變化,多個(gè)線程讀取相同的資源就是安全的。

多線程同時(shí)執(zhí)行下面的代碼可能會(huì)出錯(cuò):

public class Counter {
    protected long count = 0;
    public void add(long value){
        this.count = this.count + value;   
    }
}

想象下線程 A 和 B 同時(shí)執(zhí)行同一個(gè) Counter 對(duì)象的 add()方法,我們無(wú)法知道操作系統(tǒng)何時(shí)會(huì)在兩個(gè)線程之間切換。JVM 并不是將這段代碼視為單條指令來(lái)執(zhí)行的,而是按照下面的順序:

從內(nèi)存獲取 this.count 的值放到寄存器
將寄存器中的值增加 value
將寄存器中的值寫(xiě)回內(nèi)存

觀察線程 A 和 B 交錯(cuò)執(zhí)行會(huì)發(fā)生什么:

this.count = 0;
A: 讀取 this.count 到一個(gè)寄存器 (0)
B: 讀取 this.count 到一個(gè)寄存器 (0)
B: 將寄存器的值加 2
B: 回寫(xiě)寄存器值(2)到內(nèi)存. this.count 現(xiàn)在等于 2
A: 將寄存器的值加 3
A: 回寫(xiě)寄存器值(3)到內(nèi)存. this.count 現(xiàn)在等于 3

兩個(gè)線程分別加了 2 和 3 到 count 變量上,兩個(gè)線程執(zhí)行結(jié)束后 count 變量的值應(yīng)該等于 5。然而由于兩個(gè)線程是交叉執(zhí)行的,兩個(gè)線程從內(nèi)存中讀出的初始值都是 0。然后各自加了 2 和 3,并分別寫(xiě)回內(nèi)存。最終的值并不是期望的 5,而是最后寫(xiě)回內(nèi)存的那個(gè)線程的值,上面例子中最后寫(xiě)回內(nèi)存的是線程 A,但實(shí)際中也可能是線程 B。如果沒(méi)有采用合適的同步機(jī)制,線程間的交叉執(zhí)行情況就無(wú)法預(yù)料。

競(jìng)態(tài)條件 & 臨界區(qū)

當(dāng)兩個(gè)線程競(jìng)爭(zhēng)同一資源時(shí),如果對(duì)資源的訪問(wèn)順序敏感,就稱存在競(jìng)態(tài)條件。導(dǎo)致競(jìng)態(tài)條件發(fā)生的代碼區(qū)稱作臨界區(qū)。上例中 add()方法就是一個(gè)臨界區(qū),它會(huì)產(chǎn)生競(jìng)態(tài)條件。在臨界區(qū)中使用適當(dāng)?shù)耐骄涂梢员苊飧?jìng)態(tài)條件。

上一篇:線程池下一篇:避免死鎖