首页 > 代码库 > 《Java并发编程实战》第十章 避免活跃性危险 读书笔记

《Java并发编程实战》第十章 避免活跃性危险 读书笔记


一、死锁

所谓死锁: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。百科百科

当两个以上的运算单元,双方都在等待对方停止运行,以取得系统资源,但是没有一方提前退出时,这种状况,就称为死锁。维基百科


1. 顺序死锁
最少有两个锁,一个线程获取到A锁需要获取B锁才能进行操作,而另外一个线程获取到了B锁,需要获取A锁才能执行操作,这种情况下容易出现顺序死锁。

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并发程序设计教程》