Oracle官方併發教程之活躍度
阿新 • • 發佈:2018-12-22
一個併發應用程式能及時執行的能力稱為活躍性。本節將介紹最常見的活躍性問題:死鎖(deadlock),以及另外兩個活躍性問題:飢餓(starvation)和活鎖(livelock)。
死鎖
死鎖描述了這樣一種情景,兩個或多個執行緒永久阻塞,互相等待對方釋放資源。下面是一個例子。
Alphone和Gaston是朋友,都很講究禮節。禮節有一個嚴格的規矩,當你向一個朋友鞠躬時,你必須保持鞠躬的姿勢,直到你的朋友有機會回鞠給你。不幸的是,這個規矩沒有算上兩個朋友相互同時鞠躬的可能。
下面的應用例子,DeadLock,模擬了這個可能性。
static class Friend { private final String name; public Friend(String name) { this.name = name; } public String getName() { return this.name; } public synchronized void bow(Friend bower) { System.out.format("%s: %s" + " has bowed to me!%n", this.name, bower.getName()); bower.bowBack(this); } public synchronized void bowBack(Friend bower) { System.out.format("%s: %s" + " has bowed back to me!%n", this.name, bower.getName()); } } public static void main(String[] args) { final Friend alphonse = new Friend("Alphonse"); final Friend gaston = new Friend("Gaston"); new Thread(new Runnable() { public void run() { alphonse.bow(gaston); } }).start(); new Thread(new Runnable() { public void run() { gaston.bow(alphonse); } }).start(); } }
當DeadLock執行後,兩個執行緒極有可能阻塞,當它們嘗試呼叫bowBack方法時。沒有哪個阻塞會結束,因為每個執行緒都在等待另一個執行緒退出bow方法。
飢餓和活鎖
飢餓和活鎖並不如死鎖一般普遍,但它仍然是每個併發程式設計者可能會遇到的問題。
飢餓
飢餓是指當一個執行緒不能正常的訪問共享資源並且不能正常執行的情況。這通常在共享資源被其他“貪心”的執行緒長期時發生。舉個例子,假設一個物件提供了一個同步方法,這個方法通常需要執行很長一段時間才返回。如果一個執行緒經常呼叫這個方法,那麼其他需要同步的訪問這個物件的執行緒就經常會被阻塞。
活鎖
一個執行緒通常會有會響應其他執行緒的活動。如果其他執行緒也會響應另一個執行緒的活動,那麼就有可能發生活鎖。同死鎖一樣,發生活鎖的執行緒無法繼續執行。然而執行緒並沒有阻塞——他們在忙於響應對方無法恢復工作。這就相當於兩個在走廊相遇的人:Alphonse向他自己的左邊靠想讓Gaston過去,而Gaston向他的右邊靠想讓Alphonse過去。可見他們阻塞了對方。Alphonse向他的右邊靠,而Gaston向他的左邊靠,他們還是阻塞了對方。