今天看了第十章,描述了經典的“哲學家進餐”問題,Google 哲學家進餐 就有 ,不多解釋。先解釋一下鎖:可以這麼理解,每一個java對象都具有一個鎖標記,而這個鎖標記只能同時分配給一個線程,然後講了死鎖的產生原因:當一個線程永遠地持有一個鎖,而且其他線程都想要取得這個鎖時,那麼它們將永遠被阻塞。下面用三種介紹三種“死鎖”狀況。
一.死鎖
先看代碼
- package 併發編程;
- public class LeftRightDeadLock {
- //注意:容易發生死鎖
- private final Object left = new Object();
- private final Object right = new Object();
- public void leftRight(){
- synchronized(left){
- synchronized(right){
- doSomething();
- }
- }
- }
- public void rightLeft(){
- synchronized(right){
- synchronized(left){
- doSomethingElse();
- }
- }
- }
- private void doSomethingElse() {
- }
- private void doSomething() {
- }
- }
上面的代碼存在死鎖的風險:leftRight()和rightLeft()分別獲得left和right鎖,如果A線程調用leftRight方法,而B線程調用rightLeft方法,而且他們是交錯執行的,如下圖,他們就有會出現死鎖。
出現死鎖的原因是:兩個線程以不同順序來獲取相同的鎖。如果以相同的順序來獲取鎖,就不會出現循環的加鎖依賴性,也就不會出現死鎖。
二.動態的死鎖
看代碼
- package 併發編程;
- public class DynamicDeadLock {
- //注意:容易發生死鎖 T.T
- public void transferMoney(Account from,Account to,DollarAmount num) throws Exception{
- synchronized(from){
- synchronized(to){
- if(from.getBalance().compareTo(num) < 0){
- throw new Exception();
- }
- from.debit(num);
- to.credit(num);
- }
- }
- }
- }
上面說了:如果以相同的順序來獲取相同的鎖,就不會出現循環的加鎖依賴性,也就不會出現死鎖。在這個類中,只有一個方法,並且也是按照一個順序來獲取鎖,應該不會有什麼問題了吧?但實際上鎖的獲取是動態地取決於線程的行爲,如果同時有A、B兩個線程,A線程調用transferMoney(myBank,hisBank,1000),B線程調用transferMoney(myBank,hisBank,2000),實際上,A線程所謂的myBank其實是B線程的hisBank,A線程的hisBank其實是B線程的myBank,這樣就導致兩個線程獲取鎖的順序不一樣,產生了死鎖。
三.協作對象間的死鎖
先看兩個類
- package 併發編程;
- /**
- * 出租車類,可被出租車車隊調用
- * @author Andre
- *
- */
- public class Taxi {
- private Point location,destination;//包含兩個屬性:當前位置和目標地
- private final Dispatcher dispatcher;//所屬車隊
- public Taxi(Dispatcher dispatcher){
- this.dispatcher = dispatcher;
- }
- public synchronized Point getLocation(){
- return location;
- }
- /**
- * 通過GPS設置出租車位置
- * @param location
- */
- public synchronized void setLocation(Point location){
- this.location = location;
- if(location.equals(destination)){
- dispatcher.notifyAvailable(this);
- }
- }
- }
- package 併發編程;
- import java.awt.Image;
- import java.util.Set;
- /**
- * 出租車車隊類,指揮出租車的調動
- * @author Andre
- *
- */
- public class Dispatcher {
- private final Set<Taxi> taxis;//出租車集合
- private final Set<Taxi> availableTaxis;///可用出租車集合
- public Dispatcher(Set<Taxi> taxis , Set<Taxi> availableTaxis){
- this.taxis = taxis;
- this.availableTaxis = availableTaxis;
- }
- public synchronized void notifyAvailable(Taxi taxi) {
- availableTaxis.add(taxi);
- }
- /**
- * 獲得包含當前出租車的完整畫面
- * @return
- */
- public synchronized CarImage getImage(){
- CarImage image = new CarImage();
- for(Taxi t: taxis)
- image.drawMarker(t.getLocation());
- return image;
- }
- }