如何实现死锁(2)锁顺序死锁

概念:当一个线程永远的持有一个锁,并且其他线程都尝试获得这个锁时,那么它们将永远被阻塞

锁顺序死锁(抱死)

/**
 * 锁顺序死锁
 */
public class LeftRightDeadLock {
    private static Object left = new Object();
    private static Object right = new Object();

    public static void leftRight() {
        synchronized (left) {
            //添加延迟时间来模拟锁获取冲突
            synchronized (right) {
                System.err.println("leftRight::");
            }
        }
    }

    public static void rightLeft()  {
        synchronized (right) {
            //添加延迟时间来模拟锁获取冲突
            synchronized (left) {
                System.err.println("rightLeft::");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService exec = Executors.newFixedThreadPool(2);

        for (int i = 0; i < 1000000; i++) {
            exec.execute(()->leftRight());
            exec.execute(()->rightLeft());
        }

        exec.shutdown();
        exec.awaitTermination(10000,TimeUnit.SECONDS);
    }

}

原理图

两个线程试图以不同的顺序获得相同的锁,如果按照相同的顺序来请求锁,那么就不会出现循环依赖的加锁依赖性,因此也就不会产生死锁。

wait
永久等待
尝试锁住right
永久等待
尝试锁住left
B
锁住right
A
锁住left

如果线程都按照固定的顺序来获得锁,那么在程序中就不会出现锁顺序死锁的问题

动态的锁顺序死锁

还有一种情况是,取决于参数顺序而导致的锁顺序不同,也会出现锁顺序死锁

/**
 * 动态锁顺序死锁
 */
public class DynamicLeftRightDeadLock {

    /**
     * 转账问题
     * @param fromAccount 来源账户
     * @param toAccount 目标账户
     * @param amount 处理额度
     */
    public static void transferMoney(AtomicInteger fromAccount,
                                     AtomicInteger toAccount,
                                     Integer amount) {
        synchronized (fromAccount) {
            synchronized (toAccount) {
                System.err.println("in::");
                //该值小于参数
                if (Integer.valueOf(fromAccount.get()).compareTo(amount)<0) {
                    throw new RuntimeException("");
                }else {
                    fromAccount.addAndGet(-amount);
                    toAccount.addAndGet(amount);
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService exec = Executors.newFixedThreadPool(2);
        AtomicInteger a = new AtomicInteger(100);
        AtomicInteger b = new AtomicInteger(100);

        for (int i = 0; i < 1000000; i++) {
            exec.execute(()->transferMoney(a,b,1));
            exec.execute(()->transferMoney(b,a,1));
        }

        exec.shutdown();
        exec.awaitTermination(10000, TimeUnit.SECONDS);
    }

}

原理同上

协作死锁

协作对象持有锁的时候,调用外部的方法,那么将会出现活跃性问题。因为外部方法可能会获得其他锁(有可能就会产生死锁),如果阻塞时间过长,也会导致其他线程无法获取被当前线程持有的锁

/**
 * 协作死锁
 */
public class CooperationDeadLock {

    public static void main(String[] args) throws InterruptedException {
        ExecutorService exec = Executors.newFixedThreadPool(2);
        Dispatcher dispatcher = new Dispatcher();
        // 模拟五辆车反复进入调度
        Taxi[] taxis = new Taxi[5];
        for (int i = 0; i < 5; i++) {
            taxis[i] = new Taxi(dispatcher);
        }

        for (int i = 0; i < 100; i++) {
        //两个对象协作执行时,容易产生死锁
            exec.execute(()->{
                int index = new Random().nextInt(5);
                taxis[index].setDistance(0);
            });
            exec.execute(()->dispatcher.getSnapshot());
        }
        exec.shutdown();
        exec.awaitTermination(5, TimeUnit.SECONDS);
    }

}

/**
 * 出租车调度系统,调度获取出租车信息
 */
class Taxi {
    private int distance;
    private final Dispatcher dispatcher;

    Taxi(Dispatcher dispatcher) {
        this.dispatcher = dispatcher;
    }

    public synchronized int getDistance() {
        return distance;
    }

    public synchronized void setDistance(int distance) {
        this.distance = distance;
        if (distance==0) {
            //外部锁
            dispatcher.notifyAvailable(this);
        }
    }
}

class Dispatcher {
    private Set<Taxi> availableTaxis;

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

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

    // 获取当前可调度车辆快照
    public synchronized String getSnapshot() {
        StringBuilder builder = new StringBuilder();
        for (Taxi taxi: availableTaxis) {
            //外部锁
            builder.append(taxi+": "+taxi.getDistance()+"\n");
        }
        return builder.toString();
    }
}

原理图
同上,不同在于不是显式的使用双重锁,而是在持有锁的方法中调用了外部锁

建议使用开放调用:如果在调用某个方法的时候不需要持有锁,那么就叫做开放调用;将锁的范围控制在方法内部,具体如下

    public void setDistance(int distance) {
        boolean isArrive;
        synchronized(this) {
            this.distance = distance;
            isArrive = distance==0;
        }
        
        if (isArrive) {
            //外部锁
            dispatcher.notifyAvailable(this);
        }
    }

本内容示例主要来自《Java并发编程实战》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值