1. 程式人生 > >《Java並發編程實戰》第十章 避免活躍性危急 讀書筆記

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

for 分析 tac mage cas 系統 ron htm 發生


一、死鎖

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

百科百科

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


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. 資源死鎖 外部鎖常被忽視而導致死鎖,比如數據庫的鎖

二、死鎖的避免與診斷

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



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

三、其它活躍性危急

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


四、鎖的使用

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

存在檢測死鎖的辦法

五、參考資料:

《溫紹錦 - Java並發程序設計教程》


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