避免死锁

  死锁是指两个或者多个线程在执行过程中,因争夺资源而造成的一种相互等待的现象。也就是说每个线程已经获取了其中一个对象上的锁,而且正在等待另一个对象上的错。考虑有两个线程和两个对象的情形,如下图所示。线程1获取object1上的锁,线程2获取object2上的锁,现在线程1等待object2上的锁,线程2等待object1上的锁。每个线程都在等待另一个线程释放它所需要的锁,导致两个线程都无法继续运行(死锁)。

 死锁产生的四个必要条件

  1. 互斥条件:资源一次只能由一个线程占用
  2. 请求与保持条件:线程持有至少一个资源,并等待获取其它线程持有的资源
  3. 不剥夺条件:线程已获得的资源在未使用完之前不能被其它线程强行剥夺
  4. 循环等待条件:多个线程形成一种头尾相接的循环等待资源关系

示例:账户转账情形

package edu.cuit.avatar.concurrent;

/**
 * @author <a href="mailto:1020zhaodan@163.com">Adan</a>
 * @version 1.0
 * @date 2025/7/17 10:42
 */
public class BankDeadLockDemo {
    private static class Account{
        private String name;
        private int balance;

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

        public int getBalance() {
            return balance;
        }

        public String getName() {
            return name;
        }

        public void deposit(int amount){
            balance += amount;
        }

        public void withdraw(int amount){
            balance -= amount;
        }

        @Override
        public String toString() {
            return  name + ':' + balance;
        }
    }

    public static void transfer(Account from, Account to, int amount){
        
        synchronized (from){
            System.out.println(Thread.currentThread().getName() + " acquired lock on " + from);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            synchronized (to){
                System.out.println(Thread.currentThread().getName() + " acquired lock on " + to);
                if(from.balance >= amount){
                    from.withdraw(amount);
                    to.deposit(amount);
                    System.out.println("transfer successful!");
                } else
                    System.out.println("insufficient balance");
            }
        }
    }

    public static void main(String[] args) {
        Account peppa = new Account("Peppa", 1000);
        Account jorge = new Account("jorge", 1000);

        new Thread(()-> transfer(peppa, jorge, 500)).start();
        new Thread(()-> transfer(jorge, peppa, 500)).start();

    }
}

如何避免死锁

  使用一种称为资源排序的简单技术可以轻易地避免死锁的发生。该技术是给每一个需要锁的对象指定一个顺序,确保每个线程都按照这个顺序来获取锁。例如,在上图中,假设按照object1,object2的顺序对两个对象进行排序。采用资源排序技术,线程2必须先获取object1上的锁,然后才能获取object2上的锁。一旦线程1获取了object1上的锁,线程2必须等待object1上的锁。所以,线程1就能获取object2上的锁,不会再发生死锁的现象。

  使用资源排序技术解决上述转账问题

package edu.cuit.avatar.concurrent;

/**
 * @author <a href="mailto:1020zhaodan@163.com">Adan</a>
 * @version 1.0
 * @date 2025/7/17 10:42
 */
public class BankDeadLockDemo {
    private static class Account{
        private String name;
        private int balance;

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

        public int getBalance() {
            return balance;
        }

        public String getName() {
            return name;
        }

        public void deposit(int amount){
            balance += amount;
        }

        public void withdraw(int amount){
            balance -= amount;
        }

        @Override
        public String toString() {
            return  name + ':' + balance;
        }
    }

    public static void transfer(Account from, Account to, int amount){
     //资源排序:确保所有线程以相同顺序获取锁 Account object1
= from.name.compareTo(to.name) < 0 ? from : to; Account object2 = object1 == from ? to : from; synchronized (object1){ System.out.println(Thread.currentThread().getName() + " acquired lock on " + from); try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } synchronized (object2){ System.out.println(Thread.currentThread().getName() + " acquired lock on " + to); if(from.balance >= amount){ from.withdraw(amount); to.deposit(amount); System.out.println("transfer successful!"); } else System.out.println("insufficient balance"); } } } public static void main(String[] args) { Account peppa = new Account("Peppa", 1000); Account jorge = new Account("jorge", 1000); new Thread(()-> transfer(peppa, jorge, 500)).start(); new Thread(()-> transfer(jorge, peppa, 500)).start(); } }

检测死锁

  可以使用JDK工具检测死锁:

  1. 运行程序
  2. 使用jps命令查找java进程PID
  3. 使用jstack <pid>查看线程堆栈信息,进而了解死锁信息
posted @ 2025-07-17 11:32  Tiger-Adan  阅读(48)  评论(0)    收藏  举报