Loading

线程死锁

什么是线程死锁?

线程死锁描述的是这样一种情况:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无期限的阻塞,因此程序不可能正常终止

如下图所示,线程A 持有 资源2,线程B 持有 资源1,它们同时都想申请对方的资源,但都拿不到,所以这两个线程就会互相等待而进入死锁状态

线程死锁

死锁模拟

/**
 * 模拟线程死锁
 */
public class DeadLockMain {
    public static void main(String[] args) {
        DeadLock t1 = new DeadLock();
        DeadLock t2 = new DeadLock();
        t1.setFlag(true);
        t1.setName("A");
        t2.setFlag(false);
        t2.setName("B");
        t1.start();
        t2.start();
    }
}

class DeadLock extends Thread{
    static Object o1 = new Object();
    static Object o2 = new Object();
    private boolean flag;

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        // 死锁原因
        // 1. 如果 flag 为 true,线程A 就会先持有 o1 对象锁,然后尝试去获取 o2 对象锁
        // 2. 如果 线程A 得不到 o2 对象锁,就会 Blocked
        // 3. 如果 flag 为 false,线程B 就会先持有 o2 对象锁,然后尝试去获取 o1 对象锁
        // 4. 如果 线程B 得不到 o1 对象锁,就会 Blocked
        // 线程A 得不到 o2 对象锁,就不会释放 o1 对象锁
        // 线程B 得不到 o1 对象锁,就不会释放 o2 对象锁
        if(flag){
            synchronized (o1){
                System.out.println("线程 " + Thread.currentThread().getName() + " 进入 1...");
                synchronized (o2) {
                    System.out.println("线程 " + Thread.currentThread().getName() + " 进入 2...");
                }

            }
        }else{
            synchronized (o2){
                System.out.println("线程 " + Thread.currentThread().getName() + " 进入 3...");
                synchronized (o1){
                    System.out.println("线程 " + Thread.currentThread().getName() + " 进入 4...");
                }
            }
        }
    }
}

死锁的必要条件

要产生死锁,必须 同时 具备四个条件

  1. 互斥条件:当前资源任意一个时刻只能由一个线程占用

  2. 请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放

  3. 不剥夺条件:当前线程已获得的资源在未使用完之前不能被其它线程强行剥夺,只有自己使用完毕后才释放资源

  4. 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系

避免死锁

既然知道了死锁产生的必要条件,那么只要破坏掉其中任何一个条件,就能避免死锁

1、互斥条件

这个条件无法破坏,因为用 本身就是想让线程之间进行互斥的(临界资源需要互斥访问)


2、请求与保持条件

一个线程因请求资源而阻塞时,对已获得的资源保持不放。破坏的方式是,让当前线程主动释放已获得的资源:wait()

/**
 * 破坏死锁的必要条件-请求与保持条件
 */
public class DestroyDeadLock01Main {
    public static void main(String[] args) {
        DestroyDeadLock01 t1 = new DestroyDeadLock01();
        DestroyDeadLock01 t2 = new DestroyDeadLock01();
        t1.setFlag(true);
        t1.setName("A");
        t2.setFlag(false);
        t2.setName("B");
        t1.start();
        t2.start();
    }
}

class DestroyDeadLock01 extends Thread{
    static Object o1 = new Object();
    static Object o2 = new Object();
    private boolean flag;

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        if(flag){
            synchronized (o1){
                System.out.println("线程 " + Thread.currentThread().getName() + " 进入 1...");
                try {
                    // 主动释放对 o1 的持有
                    // 等其它线程使用完 o1 后,再来唤醒当前线程
                    o1.wait(); // wait() 方法需要其它线程手动 notify() 唤醒
                    // o1.wait(50); // wait(50) 等待 50 毫秒后,自己唤醒自己,不需要其它线程通过 notify() 手动唤醒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o2) {
                    System.out.println("线程 " + Thread.currentThread().getName() + " 进入 2...");
                }

            }
        }else{
            synchronized (o2){
                System.out.println("线程 " + Thread.currentThread().getName() + " 进入 3...");
                synchronized (o1){
                    System.out.println("线程 " + Thread.currentThread().getName() + " 进入 4...");
                    // 对 o1 使用完毕后,唤醒其它线程来争夺当前的 o1 锁
                    o1.notify();
                }
            }
        }
    }
}

3、不剥夺条件

当前线程已获得的资源在未使用完之前不能被其它线程强行剥夺,只有自己使用完毕后才释放资源。破坏的方式是,先让当前线程将获得的资源使用完,其它线程再来拿该资源

/**
 * 破坏死锁的必要条件-不剥夺条件
 */
public class DestroyDeadLock02Main {
    public static void main(String[] args) {
        DestroyDeadLock02 t1 = new DestroyDeadLock02();
        DestroyDeadLock02 t2 = new DestroyDeadLock02();
        t1.setFlag(true);
        t1.setName("A");
        t2.setFlag(false);
        t2.setName("B");
        t1.start();
        t2.start();
    }
}

class DestroyDeadLock02 extends Thread{
    static Object o1 = new Object();
    static Object o2 = new Object();
    private boolean flag;

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        if(flag){
            synchronized (o1){
                System.out.println("线程 " + Thread.currentThread().getName() + " 进入 1...");
                synchronized (o2) {
                    System.out.println("线程 " + Thread.currentThread().getName() + " 进入 2...");
                }

            }
        }else{
            try {
                // 让 B线程 先休眠 50 毫秒
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 此时 A线程 已经完成了对资源的使用,并且释放了 锁
            // 再让 B线程 去拿锁
            synchronized (o2){
                System.out.println("线程 " + Thread.currentThread().getName() + " 进入 3...");
                synchronized (o1){
                    System.out.println("线程 " + Thread.currentThread().getName() + " 进入 4...");
                }
            }
        }
    }
}

4、循环等待条件

若干线程之间形成一种头尾相接的循环等待资源关系。破坏的方式是:按序申请资源,不让若干线程形成头尾相接的循环等待资源关系

/**
 * 破坏死锁的必要条件-循环等待条件
 */
public class DestroyDeadLock03Main {
    public static void main(String[] args) {
        DestroyDeadLock03 t1 = new DestroyDeadLock03();
        DestroyDeadLock03 t2 = new DestroyDeadLock03();
        t1.setFlag(true);
        t1.setName("A");
        t2.setFlag(false);
        t2.setName("B");
        t1.start();
        t2.start();
    }
}

class DestroyDeadLock03 extends Thread{
    static Object o1 = new Object();
    static Object o2 = new Object();
    private boolean flag;

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        if(flag){
            // 线程A 先拿到 o1 的锁,线程A 执行完毕,释放 o1
            // 线程B 拿不到 o1 的锁,进入阻塞(Blocked)状态,等待 线程A 释放 o1
            synchronized (o1){
                System.out.println("线程 " + Thread.currentThread().getName() + " 进入 1...");
                synchronized (o2) {
                    System.out.println("线程 " + Thread.currentThread().getName() + " 进入 2...");
                }
            }
        }else{
            // 线程B 先拿到 o1 的锁,线程B 执行完毕,释放 o1
            // 线程A 拿不到 o1 的锁,进入阻塞(Blocked)状态,等待 线程B 释放 o1
            synchronized (o1){
                System.out.println("线程 " + Thread.currentThread().getName() + " 进入 3...");
                synchronized (o2){
                    System.out.println("线程 " + Thread.currentThread().getName() + " 进入 4...");
                }
            }
        }
    }
}
posted @ 2023-07-17 15:30  赵妹儿  阅读(11)  评论(0编辑  收藏  举报