nealmind's Blog

死锁

2020-03-19
neal

锁顺序死锁

例如有两个方法分别持有锁L,R。A线程获得L锁之后申请R锁,同时B线程获得了R锁申请L锁, 由于两个两个线程调用顺序不一致导致死锁; 这种情况需要在编码时避免多个线程以不同的顺序请求相同的锁;

动态的锁顺序死锁

例如

public voidd transferMoney(Account fromAccount, Account toAccount, BigDecimal amount){
	synchronized(fromAccount){
		synchronized(toAcccount){
			fromAccount.debit(toAcccount);
			toAcccount.credit(fromAccount);
		}
	}
}

看似每个线程都是按照相同的顺序获得锁,其实锁顺序是动态变化的,如果一个线程由A向B转账, 同时另一个线程由B向A转账,就会出现顺序死锁。 可以采用计算两个对象的hashCode,判断hashCode值的大小,始终保证大的/小的在前保证顺序; 如果两个恰好相等又不属于同一个对象,可定义一个额外的对象锁进行锁定从而保证锁顺序;

协作对象间的死锁

一般出现在持有锁A的时候调用某个外部方法,如果外部方法恰好持有锁B并且会请求锁A, 就会出现死锁。

/**
 * CooperatingDeadlock
 * <p/>
 * Lock-ordering deadlock between cooperating objects
 *
 * @author Brian Goetz and Tim Peierls
 */
@Evaluated(">_<")
public class CooperatingDeadlock {
    // Warning: deadlock-prone!
    class Taxi {
        @GuardedBy("this")
        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;
			//持有锁Taxi并调用外部方法notifyAvailable()申请锁Dispatcher
            if (location.equals( destination ))
                dispatcher.notifyAvailable( this );
        }

        public synchronized Point getDestination() {
            return destination;
        }

        public synchronized void setDestination(Point destination) {
            this.destination = destination;
        }
    }

    class Dispatcher {
        @GuardedBy("this")
        private final Set< Taxi > taxis;
        @GuardedBy("this")
        private final Set< Taxi > availableTaxis;

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

        public synchronized void notifyAvailable(Taxi taxi) {
            availableTaxis.add( taxi );
        }

        public synchronized Image getImage() {
            Image image = new Image();
			//持有锁Dispatcher并调用外部方法t.getLocation()申请锁Taxi
            for (Taxi t : taxis)
                image.drawMarker( t.getLocation() );
            return image;
        }
    }

    class Image {
        public void drawMarker(Point p) {
        }
    }

    interface Point {

    }
}

解决办法是将同步方法改为同步代码块,避免在持有锁时调用外部方法


引用:《Java并发编程实战》


***************************************************************

基于个人学习总结,如有错误,请留言告知,谢谢.


上一篇 线程中断

下一篇 JVM常用参数

Content