看到了詳細(xì)的方法實(shí)現(xiàn) ^_^
1. 兩三個線程同時修改某個對象,如果僅由訪問先后來決定結(jié)果的話,會出現(xiàn)各種結(jié)果。這種情況被稱為race condition。
2. 防止這種情況的發(fā)生,必須知道如何同步訪問(synchronize the access)。
3. javap -c -v ClassName 可以反編譯一個.class文件。
4. 鎖住對象
早期版本的Java使用synchronized關(guān)鍵詞,JDK5以后引入了ReentrantLock類。
使用ReentrantLock類保護(hù)代碼塊的基本輪廓:
myLock.lock(); // a ReentrantLock object
try
{
critical section
}
finally
{
myLock.unlock(); // make sure the lock is unlocked even if an exception is thrown
}
這樣第一個線程調(diào)用lock方法鎖住myLock后,第二個線程調(diào)用lock方法就會被block,并且得等到第一個線程調(diào)用myLock.unlock()后才能繼續(xù)。
ReentrantLock類允許被多次鎖定,它記錄了呼叫的嵌套形式。大致是這個意思,原文是
The lock is called reentrant because a thread can repeatedly acquire a lock that it already owns. The lock keeps a hold count that keeps track of the nested calls to the lock method. The thread has to call unlock for every call to lock in order to relinquish the lock. Because of this feature, code that is protected by a lock can call another method that uses the same locks.
For example, the TRansfer method calls the getTotalBalance method, which also locks the bankLock object, which now has a hold count of 2. When the getTotalBalance method exits, the hold count is back to 1. When the transfer method exits, the hold count is 0, and the thread relinquishes the lock.
In general, you will want to protect blocks of code that require multiple operations to update or inspect a data structure. You are then assured that these operations run to completion before another thread can use the same object.
5. Condition Objects
Conditon Objects用來管理得了訪問權(quán),卻實(shí)際并不能做有用功的線程。
以銀行帳戶轉(zhuǎn)帳為例,轉(zhuǎn)帳時要確定轉(zhuǎn)出源的資金數(shù)不少于要轉(zhuǎn)出的金額。
首先不能這樣簡單的寫代碼:
if (bank.getBalance(from) <= amount)
bank.transfer(from, to, amount);
這種代碼完全有可能在if語句判斷完成后,transfer之前停滯,這樣在調(diào)用transfer的時候,可能帳戶當(dāng)前的資金已經(jīng)不是if語句判斷那個時候的數(shù)目了。
也就是說,測試可行性和實(shí)際操作必須一起進(jìn)行,之間不能有中斷。
可以用一個lock把測試和操作綁定起來:
public void transfer(int from, int to, int amount)
{
bankLock.lock();
try
{
while (accounts[from] > amount)
{
// wait
. . .
}
// transfer funds
. . .
}
finally
{
bankLock.unlock();
}
}
這樣還是有問題,帳戶資金不夠移出時,會困在while塊中等待資金撥入這個帳戶,但由于bankLock已經(jīng)被鎖定,所以其他線程不能進(jìn)行撥入操作,進(jìn)入了死循環(huán)。這就是condition object產(chǎn)生的原因。
一個lock對象可以有一個或多個相關(guān)的condition object,可以通過newCondition方法獲得一個條件對象,通常用實(shí)際條件命名每個條件對象,如
class Bank
{
public Bank()
{
. . .
sufficientFunds = bankLock.newCondition();
}
. . .
private Condition sufficientFunds;
}
當(dāng)transfer方法發(fā)現(xiàn)當(dāng)前資金不夠時,調(diào)用
sufficientFunds.await();
這樣當(dāng)前線程就會停滯,并解除lock。
await方法調(diào)用后,線程進(jìn)入對應(yīng)Condition的等待區(qū),直到另一個線程調(diào)用同一Condition的signalAll方法才會解除block狀態(tài),并等待再次被線程管理器激活。
如果一個線程調(diào)用了condition.await方法,卻沒有其他線程調(diào)用condition.signalAll,這個線程就進(jìn)入了deadlock情況。如果所有其他的線程都進(jìn)入了等待區(qū),而最后一個線程也調(diào)用了condition.await,那么整個程序就掛起了。
因此最好的調(diào)用signalAll的時機(jī)是在每次對象的狀態(tài)被改變,而這個改變有可能使得等待區(qū)的線程有進(jìn)展的時候。如Bank中每次成功轉(zhuǎn)帳之后。
另一個方法,signal,僅僅從等待區(qū)中隨機(jī)選擇一個進(jìn)程并釋放。
注意:線程只能在獲得了lock權(quán)以后才能調(diào)用condition的await, signal, 和signalAll。