淺談Java多線程編程中Boolean常量的同步問題
在JAVA中通過synchronized語句可以實(shí)現(xiàn)多線程并發(fā)。使用同步代碼塊,JVM保證同一時(shí)間只有一個(gè)線程可以擁有某一對(duì)象的鎖。鎖機(jī)制實(shí)現(xiàn)了多個(gè)線程安全地對(duì)臨界資源進(jìn)行訪問。
同步代碼寫法如下:
代碼1:
Object obj = new Object();
...
synchronized(obj) {
//TODO: 訪問臨界資源
}
JAVA的多線程總是充滿陷阱,如果我們用Boolean作為被同步的對(duì)象,可能會(huì)出現(xiàn)以下兩種情況:
一. 以為對(duì)一個(gè)對(duì)象加鎖,實(shí)際同步的是不同對(duì)象。
代碼2:
private volatile Boolean isTrue = false;
publich void aMethod() {
...
synchronized(isTrue) {
isTrue = !isTrue;
//TODO: 訪問臨界資源
isTrue = !isTrue;
}
...
}
咋一看上面的代碼沒有問題,由于使用了synchronized(isTrue)同一時(shí)間只能有一個(gè)線程訪問臨界資源,但事實(shí)并不是這樣。因?yàn)閒alse和true這兩個(gè)常量對(duì)應(yīng)著兩個(gè)不同的對(duì)象。當(dāng)isTrue產(chǎn)生變化時(shí),很可能導(dǎo)致不同的線程同步了不同的對(duì)象。JAVA的自動(dòng)裝箱會(huì)將false變?yōu)锽oolean.FALSE,將true變?yōu)锽oolean.TRUE(同時(shí)這也說明了此處若將false改為Boolean.FALSE其結(jié)果也是一樣的)。寫一個(gè)以上情況的測(cè)試代碼如下:
代碼3:
public class BooleanTest {
private volatile Boolean isTrue = Boolean.FALSE; //此處用false也一樣
public void aMethod() {
for(int i=0;i<10;i++) {
Thread t = new Thread() {
public void run() {
synchronized(isTrue) {
isTrue = !isTrue;
System.out.println(Thread.currentThread().getName() + " - isTrue=" + isTrue);
try{
Double ran = 1000 * Math.random();
Thread.sleep(ran.intValue());
}catch(InterruptedException e) {}
if(!isTrue) System.out.println(Thread.currentThread().getName() + " - Oh, No!");
isTrue = !isTrue;
}
}
};
t.start();
}
}
public static void main(String... args) {
BooleanTest bt = new BooleanTest();
bt.aMethod();
}
}
運(yùn)行以上代碼,不時(shí)的會(huì)看到 " - Oh, No!",表示不同的線程同時(shí)進(jìn)入到synchronized代碼塊中。
二. 以為同步的是不同對(duì)象,實(shí)際是一個(gè)對(duì)象。
有時(shí)候我們可能希望在多個(gè)對(duì)象上進(jìn)行同步,如果使用了Boolean作為被同步對(duì)象,很可能會(huì)導(dǎo)致本來應(yīng)該沒有關(guān)系的兩個(gè)同步塊使用了相同對(duì)象的鎖。示例如下:
代碼4:
private volatile Boolean aBoolean = Boolean.FALSE;
private volatile Boolean anotherBoolean = false;
public void aMethod() {
...
synchronized(aBoolean) {
//TODO: 訪問臨界資源1
}
...
}
public void anotherMethod() {
...
synchronized(anotherBoolean) {
//TODO: 訪問臨界資源2
}
...
}
假設(shè)原本aMethod和anotherMethod分別會(huì)被兩組沒有關(guān)系的線程調(diào)用。但是由于Boolean.FALSE和false指向的是同一個(gè)對(duì)象,可能導(dǎo)致對(duì)臨界資源2的訪問被臨界資源1阻塞了(反之亦然)。
以上兩種情況說明,在使用同步塊時(shí),盡量不用使用Boolean對(duì)象作為被同步對(duì)象,不然可能會(huì)出現(xiàn)意想不到的問題,或者對(duì)以后的代碼修改造成陷阱。
從此也可以看出,任何對(duì)常量的同步都是有風(fēng)險(xiǎn)的。如果一定要對(duì) Boolean 進(jìn)行同步,一定要用 new 操作符來創(chuàng)建 Boolean 對(duì)象。
相關(guān)文章
spring為類的靜態(tài)屬性實(shí)現(xiàn)注入實(shí)例方法
在本篇文章里小編給大家整理的是關(guān)于spring為類的靜態(tài)屬性實(shí)現(xiàn)注入實(shí)例方法,有需要的朋友們可以參考下。2019-10-10
spring cloud Feign使用@RequestLine遇到的坑
這篇文章主要介紹了spring cloud Feign使用@RequestLine遇到的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
如何通過SpringBoot實(shí)現(xiàn)商城秒殺系統(tǒng)
這篇文章主要介紹了如何通過SpringBoot實(shí)現(xiàn)商城秒殺系統(tǒng),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
Java8新特性之泛型的目標(biāo)類型推斷_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
泛型是Java SE 1.5的新特性,泛型的本質(zhì)是參數(shù)化類型,也就是說所操作的數(shù)據(jù)類型被指定為一個(gè)參數(shù)。下面通過本文給分享Java8新特性之泛型的目標(biāo)類型推斷,感興趣的朋友參考下吧2017-06-06
MyBatisPlus使用${ew.customSqlSegment}別名問題解決
在使用MyBatisPlus進(jìn)行連表查詢時(shí),可能遇到因${ew.customSqlSegment}無法加別名的問題,本文就來介紹一下如何解決,感興趣的可以了解一下2024-10-10
springboot IDEA啟動(dòng)兩個(gè)端口服務(wù)nginx負(fù)載過程
這篇文章主要介紹了springboot IDEA啟動(dòng)兩個(gè)端口服務(wù)nginx負(fù)載過程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-03-03
Java中實(shí)現(xiàn)Comparator接口和用法實(shí)例(簡(jiǎn)明易懂)
這篇文章主要介紹了Java中實(shí)現(xiàn)Comparator接口和用法實(shí)例(簡(jiǎn)明易懂),本文給出實(shí)現(xiàn)Comparator接口的實(shí)例和使用這個(gè)接口的代碼實(shí)例,需要的朋友可以參考下2015-05-05
SPRINGMVC JSON數(shù)據(jù)交互如何實(shí)現(xiàn)
這篇文章主要介紹了SPRINGMVC JSON數(shù)據(jù)交互如何實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06
SpringBoot解決數(shù)據(jù)庫時(shí)間和返回時(shí)間格式不一致的問題
這篇文章主要介紹了SpringBoot解決數(shù)據(jù)庫時(shí)間和返回時(shí)間格式不一致的問題,文章通過代碼示例和圖文結(jié)合的方式講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)和工作有一定的幫助,需要的朋友可以參考下2024-03-03

