java并发编程(九): 避免活跃性危险

in 编程
关注公众号【好便宜】( ID:haopianyi222 ),领红包啦~
阿里云,国内最大的云服务商,注册就送数千元优惠券:https://t.cn/AiQe5A0g
腾讯云,良心云,价格优惠: https://t.cn/AieHwwKl
搬瓦工,CN2 GIA 优质线路,搭梯子、海外建站推荐: https://t.cn/AieHwfX9

避免活跃性危险:

死锁:

锁顺序死锁:

如上面哲学家进餐有可能发生下面的情况:

/**
 * 容易因为获取锁的顺序导致死锁
 */
public class LeftRightDeadLock {
	private final Object left = new Object();
	private final Object right = new Object();
	
	public void leftRight(){
		synchronized(left){
			synchronized(right){
				// to do sth.
			}
		}
	}
	
	public void rightLeft(){
		synchronized(right){
			synchronized(left){
				// to do sth.
			}
		}
	}
}

动态的锁顺序死锁:

public void transferMoney(Account fromAccount, Account toAccount, int money){
	synchronized (fromAccount) {
		synchronized(toAccount){
			if (fromAccount.getBalance() > money){
				//余额不足
			} else{
				fromAccount.debit(money);
				toAccount.credit(money);
			}
		}
	}
}

当我们以下面这种方式调用,就有可能出现死锁:

transferMoney(a1, a2, money);
transferMoney(a2, a1, money);

要解决这种问题,就得使内部以相同的顺序加锁,无论外部怎么调用。

/**
 * 用于当输入参数的hash值一样时使用
 */
private static final Object tieLock = new Object();
	
public static void transferMoney(final Account fromAccount, 
		final Account toAccount, final int money){
	class Helper{
		public void transfer(){
			if (fromAccount.getBalance() < money){
				//余额不足
			} else{
				fromAccount.debit(money);
				toAccount.credit(money);
			}
		}
	}
		
	int fromHash = System.identityHashCode(fromAccount);
	int toHash = System.identityHashCode(toAccount);
		
	//无论客户端怎么传入参数,我们都以先锁定hash值小的,再锁定hash大的
	//也可以利用业务中排序关系,如Account的编号等来比较
	if (fromHash < toHash){
		synchronized (fromAccount){
			synchronized (toAccount) {
				new Helper().transfer();
			}
		}
	} else if (fromHash >  toHash){
		synchronized (toAccount){
			synchronized (fromAccount) {
				new Helper().transfer();
			}
		}
	} else { //hash值相等, 情况很小
		synchronized (tieLock) {
			synchronized (fromAccount) {
				synchronized (toAccount) {
					new Helper().transfer();
				}
			}
		}
	}
}

在协作对象之间发生的死锁:

class Taxi {
	private Point location; 
	private Point 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<>();
		avaliableTaxis = new HashSet<>();
	}

	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;
	}
}

上面的
setLocation
getImage就有可能发生死锁现象:setLocation获取到Taxi对象锁后,在dispacher.notifiyAvaliable()时需要dispatcher锁,而getImage获取到dispacher锁后,t.getLocation要求Taxi锁。

开放调用:

/**
 * 通过公开调用来避免在相互协作的对象之间产生死锁
 */
public class OpenCall {
	class Taxi {
		private Point location; 
		private Point destination;
		private final Dispatcher dispatcher;

		public Taxi(Dispatcher dispatcher) {
			this.dispatcher = dispatcher;
		}
		
		public synchronized Point getLocation(){
			return location;
		}
		
		public void setLocation(Point location){
			boolean reachedDestination;
			synchronized(this){
				this.location = location;
				reachedDestination = location.equals(destination);
			}
			if (reachedDestination){
				dispatcher.notifyAvaliable(this); //这里持有dispatcher锁,但已释放taxi锁
			}
		}
	}
	
	class Dispatcher {
		private final Set<Taxi> taxis;
		private final Set<Taxi> avaliableTaxis;
		
		public Dispatcher(){
			taxis = new HashSet<>();
			avaliableTaxis = new HashSet<>();
		}

		public synchronized void notifyAvaliable(Taxi taxi) {
			avaliableTaxis.add(taxi);
		}
		
		public Image getImage(){
			Set<Taxi> copy;
			synchronized (this) {
				copy = new HashSet<Taxi>(taxis);
			}
			Image image = new Image();
			for (Taxi t :copy){
				image.drawMarker(t.getLocation());//调用外部方法前已释放锁
			}
			return image;
		}
	}
}

资源死锁:

死锁的避免与诊断:

支持定时的锁:

通过线程转储信息来分析死锁:

其他活跃性危险:

饥饿:

糟糕的响应性:

活锁:

不吝指正。

关注公众号【好便宜】( ID:haopianyi222 ),领红包啦~
阿里云,国内最大的云服务商,注册就送数千元优惠券:https://t.cn/AiQe5A0g
腾讯云,良心云,价格优惠: https://t.cn/AieHwwKl
搬瓦工,CN2 GIA 优质线路,搭梯子、海外建站推荐: https://t.cn/AieHwfX9
扫一扫关注公众号添加购物返利助手,领红包
Comments are closed.

推荐使用阿里云服务器

超多优惠券

服务器最低一折,一年不到100!

朕已阅去看看