死锁-举例

首先回顾我们对死锁的定义,通俗易懂地说就是双方在占有自己手上的资源时,需要对方手上的资源才能继续下去,但是双方又不愿意主动放弃自己手上的资源

用一个生活中通俗易懂的例子就是:对方道歉我就道歉

这个模型用代码实现最简单的框架是这样

public class MustDeadLock implements Runnable{

    public int flag;
    static final Object o1 = new Object();
    static final Object o2 = new Object();

    @Override
    public void run() {
        System.out.println("线程"+Thread.currentThread().getName() + "的flag为" + flag);

        if (flag == 1) {
            synchronized (o1) {
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                synchronized (o2) {
                    System.out.println("线程1获得了两把锁");
                }
            }
        }

        if (flag == 2) {
            synchronized (o2) {
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                synchronized (o1) {
                    System.out.println("线程2获得了两把锁");
                }
            }
        }
    }

    public static void main(String[] args) {
        TransferMoney r1 = new TransferMoney();
        TransferMoney r2 = new TransferMoney();
        r1.flag = 1;
        r2.flag = 2;
        Thread t1 = new Thread(r1, "t1");
        Thread t2 = new Thread(r2, "t2");
        t1.start();
        t2.start();
    }
}

然后既然是举例我们需要对这个模型添加一点实际的情景,比如转账

public class TransferMoney implements Runnable {

    static class Account {
        int balance;

        public Account(int balance) {
            this.balance = balance;
        }
    }

    public int flag;
    static Account account1 = new Account(1000);
    static Account account2 = new Account(500);

    @Override
    public void run() {
        // 这里其实是两笔互相转账的操作导致的死锁
        if (flag == 1) transferMoney(account1, account2, 500);
        if (flag == 2) transferMoney(account2, account1, 500);
    }

    private void transferMoney(Account from, Account to, int amount) {
        synchronized (from) {
            System.out.println(Thread.currentThread().getName() + "获取到第一把锁");
            // 确保两把锁分别被不同的线程先拿到,发生死锁
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (to) {
                System.out.println(Thread.currentThread().getName() + "获取到第二把锁");
            }
            if (from.balance - amount < 0) {
                System.out.println("余额不足,转账失败。");
                return;
            }
            from.balance -= amount;
            to.balance += amount;
            System.out.println("成功转账" + amount + "元");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TransferMoney r1 = new TransferMoney();
        TransferMoney r2 = new TransferMoney();
        r1.flag = 1;
        r2.flag = 2;
        Thread t1 = new Thread(r1, "第一笔转账");
        Thread t2 = new Thread(r2, "第二笔转账");
        t1.start();
        t2.start();
        // 这里的两个join操作是为了保证下面打印余额前两笔转账完成
        t1.join();
        t2.join();
        System.out.println("账户1的余额为:" + account1.balance);
        System.out.println("账户2的余额为:" + account2.balance);
    }
}

死锁检测与避免

# 获取到Java进程的 pid
jps
# 检查线程死锁情况,以及各个线程具体持有的锁情况
jstack 8359

解决思路:

  1. 使用主键或者哈希,确保每次转账尝试获取锁时,尝试获取的都是主键或者哈希值最小的那个锁

引用:银行转账问题(死锁)

posted @ 2024-07-24 15:21  YaosGHC  阅读(13)  评论(0编辑  收藏  举报