6.2多線程的互斥與同步
臨界資源問題
前面所提到的線程都是獨立的,而且異步執(zhí)行,也就是說每個線程都包含了運行時所需要的數(shù)據(jù)或方法,而不需要外部的資源或方法,也不必關(guān)心其它線程的狀態(tài)或行為。但是經(jīng)常有一些同時運行的線程需要共享數(shù)據(jù),此時就需考慮其他線程的狀態(tài)和行為,否則就不能保證程序的運行結(jié)果的正確性。例6.4說明了此問題。
例6.4
class stack{
int idx=0; //堆棧指針的初始值為0
char[ ] data = new char[6]; //堆棧有6個字符的空間
public void push(char c){ //壓棧操作
data[idx] = c; //數(shù)據(jù)入棧
idx + +; //指針向上移動一位
}
public char pop(){ //出棧操作
idx - -; //指針向下移動一位
return data[idx]; //數(shù)據(jù)出棧
}
}
兩個線程A和B在同時使用Stack的同一個實例對象,A正在往堆棧里push一個數(shù)據(jù),B則要從堆棧中pop一個數(shù)據(jù)。如果由于線程A和B在對Stack對象的操作上的不完整性,會導(dǎo)致操作的失敗,具體過程如下所示:
1) 操作之前
data = | p | q | | | | | idx=2
2) A執(zhí)行push中的第一個語句,將r推入堆棧;
data = | p | q | r | | | | idx=2
3) A還未執(zhí)行idx++語句,A的執(zhí)行被B中斷,B執(zhí)行pop方法,返回q:
data = | p | q | r | | | | idx=1
4〕A繼續(xù)執(zhí)行push的第二個語句:
data = | p | q | r | | , | | idx=2
最后的結(jié)果相當于r沒有入棧。產(chǎn)生這種問題的原因在于對共享數(shù)據(jù)訪問的操作的不完整性。
臨界資源問題
前面所提到的線程都是獨立的,而且異步執(zhí)行,也就是說每個線程都包含了運行時所需要的數(shù)據(jù)或方法,而不需要外部的資源或方法,也不必關(guān)心其它線程的狀態(tài)或行為。但是經(jīng)常有一些同時運行的線程需要共享數(shù)據(jù),此時就需考慮其他線程的狀態(tài)和行為,否則就不能保證程序的運行結(jié)果的正確性。例6.4說明了此問題。
例6.4
class stack{
int idx=0; //堆棧指針的初始值為0
char[ ] data = new char[6]; //堆棧有6個字符的空間
public void push(char c){ //壓棧操作
data[idx] = c; //數(shù)據(jù)入棧
idx + +; //指針向上移動一位
}
public char pop(){ //出棧操作
idx - -; //指針向下移動一位
return data[idx]; //數(shù)據(jù)出棧
}
}
兩個線程A和B在同時使用Stack的同一個實例對象,A正在往堆棧里push一個數(shù)據(jù),B則要從堆棧中pop一個數(shù)據(jù)。如果由于線程A和B在對Stack對象的操作上的不完整性,會導(dǎo)致操作的失敗,具體過程如下所示:
1) 操作之前
data = | p | q | | | | | idx=2
2) A執(zhí)行push中的第一個語句,將r推入堆棧;
data = | p | q | r | | | | idx=2
3) A還未執(zhí)行idx++語句,A的執(zhí)行被B中斷,B執(zhí)行pop方法,返回q:
data = | p | q | r | | | | idx=1
4〕A繼續(xù)執(zhí)行push的第二個語句:
data = | p | q | r | | , | | idx=2
最后的結(jié)果相當于r沒有入棧。產(chǎn)生這種問題的原因在于對共享數(shù)據(jù)訪問的操作的不完整性。