java并發(fā)的資源

允許被多個線程同時執(zhí)行的代碼稱作線程安全的代碼。線程安全的代碼不包含競態(tài)條件。當多個線程同時更新共享資源時會引發(fā)競態(tài)條件。因此,了解Java線程執(zhí)行時共享了什么資源很重要。

局部變量

局部變量存儲在線程自己的棧中。也就是說,局部變量永遠也不會被多個線程共享。所以,基礎(chǔ)類型的局部變量是線程安全的。下面是基礎(chǔ)類型的局部變量的一個例子:

publicvoid someMethod(){


? longthreadSafeInt = 0;

? threadSafeInt++;

}

局部的對象引用

對象的局部引用和基礎(chǔ)類型的局部變量不太一樣。盡管引用本身沒有被共享,但引用所指的對象并沒有存儲在線程的棧內(nèi)。所有的對象都存在共享堆中。如果在某個方法中創(chuàng)建的對象不會逃逸出(譯者注:即該對象不會被其它方法獲得,也不會被非局部變量引用到)該方法,那么它就是線程安全的。實際上,哪怕將這個對象作為參數(shù)傳給其它方法,只要別的線程獲取不到這個對象,那它仍是線程安全的。下面是一個線程安全的局部引用樣例:

publicvoid someMethod(){


? LocalObject localObject =new LocalObject();

? localObject.callMethod();

? method2(localObject);

}publicvoid method2(LocalObject localObject){

? localObject.setValue("value");

}

樣例中LocalObject對象沒有被方法返回,也沒有被傳遞給someMethod()方法外的對象。每個執(zhí)行someMethod()的線程都會創(chuàng)建自己的LocalObject對象,并賦值給localObject引用。因此,這里的LocalObject是線程安全的。事實上,整個someMethod()都是線程安全的。即使將LocalObject作為參數(shù)傳給同一個類的其它方法或其它類的方法時,它仍然是線程安全的。當然,如果LocalObject通過某些方法被傳給了別的線程,那它就不再是線程安全的了。

對象成員

對象成員存儲在堆上。如果兩個線程同時更新同一個對象的同一個成員,那這個代碼就不是線程安全的。下面是一個樣例:

publicclass NotThreadSafe{

? ? StringBuilder builder =new StringBuilder();


? ? public add(String text){

? ? ? ? this.builder.append(text);

? ? }? ?

}

如果兩個線程同時調(diào)用同一個NotThreadSafe實例上的add()方法,就會有競態(tài)條件問題。例如:

NotThreadSafe sharedInstance =new NotThreadSafe();newThread(new MyRunnable(sharedInstance)).start();newThread(new MyRunnable(sharedInstance)).start();publicclassMyRunnableimplements Runnable{

? NotThreadSafe instance =null;


? public MyRunnable(NotThreadSafe instance){

? ? this.instance = instance;

? }

? publicvoid run(){

? ? this.instance.add("some text");

? }

}

注意兩個MyRunnable共享了同一個NotThreadSafe對象。因此,當它們調(diào)用add()方法時會造成競態(tài)條件。

當然,如果這兩個線程在不同的NotThreadSafe實例上調(diào)用call()方法,就不會導(dǎo)致競態(tài)條件。下面是稍微修改后的例子:

newThread(newMyRunnable(new NotThreadSafe())).start();newThread(newMyRunnable(newNotThreadSafe())).start();

現(xiàn)在兩個線程都有自己單獨的NotThreadSafe對象,調(diào)用add()方法時就會互不干擾,再也不會有競態(tài)條件問題了。所以非線程安全的對象仍可以通過某種方式來消除競態(tài)條件。

線程控制逃逸規(guī)則

線程控制逃逸規(guī)則可以幫助你判斷代碼中對某些資源的訪問是否是線程安全的。

如果一個資源的創(chuàng)建,使用,銷毀都在同一個線程內(nèi)完成,

且永遠不會脫離該線程的控制,則該資源的使用就是線程安全的。

資源可以是對象,數(shù)組,文件,數(shù)據(jù)庫連接,套接字等等。Java中你無需主動銷毀對象,所以“銷毀”指不再有引用指向?qū)ο蟆?/p>

即使對象本身線程安全,但如果該對象中包含其他資源(文件,數(shù)據(jù)庫連接),整個應(yīng)用也許就不再是線程安全的了。比如2個線程都創(chuàng)建了各自的數(shù)據(jù)庫連接,每個連接自身是線程安全的,但它們所連接到的同一個數(shù)據(jù)庫也許不是線程安全的。比如,2個線程執(zhí)行如下代碼:

檢查記錄X是否存在,如果不存在,插入X

如果兩個線程同時執(zhí)行,而且碰巧檢查的是同一個記錄,那么兩個線程最終可能都插入了記錄:

線程1檢查記錄X是否存在。檢查結(jié)果:不存在

線程2檢查記錄X是否存在。檢查結(jié)果:不存在

線程1插入記錄X

線程2插入記錄X

同樣的問題也會發(fā)生在文件或其他共享資源上。因此,區(qū)分某個線程控制的對象是資源本身,還是僅僅到某個資源的引用很重要。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容