Java 多线程学习笔记二十二(死锁)

内容来自B站【狂神说Java】多线程详解

死锁

  • 多个线程各自占有一些公共资源,并且互相等待其他线程释放占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题。

避免方法

  • 产生死锁的四个必要条件

    1. 互斥条件:一个资源每次只能被一个进程使用。
    2. 请求与保持条件:一个线程因请求资源而阻塞时,对方获得的资源保持不放。
    3. 不剥夺条件:线程已获得的资源,在未使用完之前,不能进行剥夺。
    4. 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系。

上面列出了死锁的四个必要条件,我们只要想办法破其中的任意一个或多个条件就可以避免死锁发生。

代码演示

下列代码可以说明问题,但例子不太恰当,实际上化妆的确需要同时掌握口红和镜子。

一、出现死锁

package com.example.demo.thread.sync;

public class DeadLock {

    public static void main(String[] args) {
        // 灰姑娘要先拿口红
        Makeup makeup = new Makeup(0, "灰姑娘");
        // 白雪公主要先拿镜子
        Makeup makeup1 = new Makeup(1, "白雪公主");

        makeup.start();
        makeup1.start();
    }

}

/**
 * 口红
 */
class Lipstick {

}

/**
 * 镜子
 */
class Mirror {

}

class Makeup extends Thread {

    // 需要的资源只有一份,用static来保证只有一份
    static final Lipstick lipstick = new Lipstick();
    static final Mirror mirror = new Mirror();

    int choice;// 选择要先拿的物品,0:先拿口红,1:先拿镜子
    String girlName;// 使用化妆品的人

    public Makeup(int choice, String girlName) {
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        try {
            this.makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 化妆,互相持有对方的锁,就是需要拿到对方的资源
     */
    private void makeup() throws InterruptedException {
        if (this.choice == 0) {// 先拿口红
            synchronized (lipstick) {
                System.out.println(this.girlName + "拿起了口红");
                // 模拟用完了口红
                Thread.sleep(1000);
                System.out.println(this.girlName + "用完了口红");
                // 想拿到镜子
                synchronized (mirror) {
                    System.out.println(this.girlName + "拿起了镜子");
                    // 模拟用完了镜子
                    Thread.sleep(2000);
                    System.out.println(this.girlName + "用完了镜子");
                    System.out.println(this.girlName + "化完了妆");
                }
            }
        } else {// 先拿镜子
            synchronized (mirror) {
                System.out.println(this.girlName + "拿起了镜子");
                // 模拟用完了镜子
                Thread.sleep(2000);
                System.out.println(this.girlName + "用完了镜子");
                // 想拿到口红
                synchronized (lipstick) {
                    System.out.println(this.girlName + "拿起了口红");
                    // 模拟用完了口红
                    Thread.sleep(1000);
                    System.out.println(this.girlName + "用完了口红");
                    System.out.println(this.girlName + "化完了妆");
                }
            }
        }
    }

}


输出

灰姑娘拿起了口红
白雪公主拿起了镜子
灰姑娘用完了口红
白雪公主用完了镜子

二、避免死锁

package com.example.demo.thread.sync;

public class DeadLock {

    public static void main(String[] args) {
        // 灰姑娘要先拿口红
        Makeup makeup = new Makeup(0, "灰姑娘");
        // 白雪公主要先拿镜子
        Makeup makeup1 = new Makeup(1, "白雪公主");

        makeup.start();
        makeup1.start();
    }

}

/**
 * 口红
 */
class Lipstick {

}

/**
 * 镜子
 */
class Mirror {

}

class Makeup extends Thread {

    // 需要的资源只有一份,用static来保证只有一份
    static final Lipstick lipstick = new Lipstick();
    static final Mirror mirror = new Mirror();

    int choice;// 选择要先拿的物品,0:先拿口红,1:先拿镜子
    String girlName;// 使用化妆品的人

    public Makeup(int choice, String girlName) {
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        try {
            this.makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 化妆,互相持有对方的锁,就是需要拿到对方的资源
     */
    private void makeup() throws InterruptedException {
        if (this.choice == 0) {// 先拿口红
            synchronized (lipstick) {
                System.out.println(this.girlName + "拿起了口红");
                // 模拟用完了口红
                Thread.sleep(1000);
                System.out.println(this.girlName + "用完了口红");
            }
            // 想拿到镜子
            synchronized (mirror) {
                System.out.println(this.girlName + "拿起了镜子");
                // 模拟用完了镜子
                Thread.sleep(2000);
                System.out.println(this.girlName + "用完了镜子");
                System.out.println(this.girlName + "化完了妆");
            }
        } else {// 先拿镜子
            synchronized (mirror) {
                System.out.println(this.girlName + "拿起了镜子");
                // 模拟用完了镜子
                Thread.sleep(2000);
                System.out.println(this.girlName + "用完了镜子");
            }
            // 想拿到口红
            synchronized (lipstick) {
                System.out.println(this.girlName + "拿起了口红");
                // 模拟用完了口红
                Thread.sleep(1000);
                System.out.println(this.girlName + "用完了口红");
                System.out.println(this.girlName + "化完了妆");
            }
        }
    }

}

输出

灰姑娘拿起了口红
白雪公主拿起了镜子
灰姑娘用完了口红
白雪公主用完了镜子
白雪公主拿起了口红
灰姑娘拿起了镜子
白雪公主用完了口红
白雪公主化完了妆
灰姑娘用完了镜子
灰姑娘化完了妆
posted @ 2022-01-02 12:20  君子键  阅读(18)  评论(0)    收藏  举报