《Java併發編程實戰》第十章 避免活躍性危急 讀書筆記


1、死鎖

所謂死鎖: 是指兩個或兩個以上的進程在運行過程當中,因爭奪資源而形成的一種互相等待的現象,若無外力做用,它們都將沒法推動下去。

百科百科

java

當兩個以上的運算單元,兩方都在等待對方中止執行,以取得系統資源,但是沒有一方提早退出時。這樣的情況。就稱爲死鎖。 維基百科


1. 順序死鎖
最少有兩個鎖。一個線程獲取到A鎖需要獲取B鎖才幹進行操做,而另一個線程獲取到了B鎖,需要獲取A鎖才幹運行操做,這樣的狀況下easy出現順序死鎖。


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) {
				// doSomething();
			}
		}
	}
}



2. 動態的鎖順序死鎖
	public void transferMoney(Account fromAccount, Account toAccount, DollarAmount anount)
			throws InsufficientResourcesException {
		synchronized (fromAccount) {
			synchronized (toAccount) {
				if (fromAccount.getBalance().compareTo(amount) < 0) {
					throw new InsufficientResourcesException();
				} else {
					fromAccount.debit(anount);
					toAccount.credit(anount);
				}
			}
		}
	}

A: transferMoney(myAccount, yourAccount, 10);
B: transferMoney(yourAccount, myAccount, 20);

由外部傳入的變量所有鎖的條件,但是由以上傳入的變量可以看到,這樣的狀況下會出現一個線程先獲取myAccount鎖在申請yourAccount鎖,而另一個線程相反先獲取yourAccount鎖在申請myAccount鎖。

	private static final Object tieLock = new Object();

	public void transferMoney(final Account fromAccount, final Account toAccount, final DollarAmount anount)
			throws InsufficientResourcesException {
		class Helper{
		    public void transfer() throws InsufficientResourcesException {
		        if (fromAccount.getBalance().compareTo(amount) < 0){
		        	throw new InsufficientResourcesException();
		        } else{
					fromAccount.debit(anount);
					toAccount.credit(anount);
		        }
		    }
		}

		int fromHash = System.identityHashCode(fromAccount);
		int toHash = System.identityHashCode(toAccount);
	
		if (fromHash < toHash){
		    synchronized (fromAccount){
		        synchronized (toAccount) {
		            new Helper().transfer();
		        }
		    }
		} else if (fromHash >  toHash){
		    synchronized (toAccount){
		        synchronized (fromAccount) {
		            new Helper().transfer();
		        }
		    }
		} else {
		    synchronized (tieLock) {
		        synchronized (fromAccount) {
		            synchronized (toAccount) {
		                new Helper().transfer();
		            }
		        }
		    }
		}
	}

3. 在協做對象之間發生的死鎖
class Taxi {

	private Point location, destination;
	private final Dispatcher dispatcher;

	public Taxi(Dispatcher dispatcher) {
	    this.dispatcher = dispatcher;
	}
	
	public synchronized Point getLocation(){
	    return location;
	}

	public synchronized void setLocation(Point location){
	    this.location = location;
	    if (location.equals(destination)){
	        dispatcher.notifyAvaliable(this);
	    }
	}

}

 

class Dispatcher {

	private final Set<Taxi> taxis;
	private final Set<Taxi> avaliableTaxis;

	public Dispatcher(){
	    taxis = new HashSet<Taxi>();
	    avaliableTaxis = new HashSet<Taxi>();
	}

	public synchronized void notifyAvaliable(Taxi taxi) {
	    avaliableTaxis.add(taxi);
	}
	
	public synchronized Image getImage(){
	    Image image = new Image();
	    for (Taxi t :taxis){
	        image.drawMarker(t.getLocation());
	    }
	    return image;
	}

}

4. 開放調用
 -- 待填充

5. 資源死鎖
外部鎖常被忽視而致使死鎖,好比數據庫的鎖

2、死鎖的避免與診斷

1. 支持定時的死鎖
存在一些預防死鎖的手段。比方Lock的tryLock,JDK 7中引入的Phaser等。

2. 經過線程轉儲信息來分析死鎖
經過Dump線程的StackTrace,好比linux下運行命令 kill -3 <pid>,或者jstack –l <pid>,或者使用Jconsole鏈接上去查看線程的StackTrace。由此來診斷死鎖問題。



3、其它活躍性危急

1. 飢餓
2. 糟糕的響應性
3. 活鎖



4、鎖的使用

使用支持CAS的數據結構。避免使用鎖,如:AtomicXXX、ConcurrentMap、CopyOnWriteList、ConcurrentLinkedQueue
死鎖經常是沒法全然避免的,鴕鳥策略被很是多基礎框架所採用。
存在檢測死鎖的辦法


5、參考資料:

《溫紹錦 - Java併發程序設計教程》
相關文章
相關標籤/搜索