0
点赞
收藏
分享

微信扫一扫

多线程--死锁

凯约 2022-02-19 阅读 35

什么是死锁?

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

例子:

package com.company;
//镜子mirror
//口红lipstick

class LipsTick {
}

class Mirror {
}

 class makeUp extends Thread {
     static LipsTick lipsTick = new LipsTick();//static保证只有一份共享资源,被该类所有的实例共享
    static Mirror mirror = new Mirror(); //内存地址是不变的,就算重新建立该类的对象,但是该对象的地址不变
    int id;
    String name;

    makeUp(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public void makeUp() throws InterruptedException {
        if (this.id == 0) {
            synchronized (mirror) {         //获得了镜子,独占该镜子资源
                System.out.println(this.name + "获得了镜子");
                Thread.sleep(1000);      //阻塞该线程,让另一线程进入,不会释放锁
                synchronized (lipsTick) {  //还想要口红
                    System.out.println(this.name + "获得了口红");
                }
            }
        } else {
            synchronized (lipsTick) { //进入第二条线程,获得口红,独占该口红资源
                System.out.println(this.name+ "获得了口红");
                Thread.sleep(1000);   //阻塞该线程,让另一线程进入
                synchronized (mirror) {
                    System.out.println(this.name + "获得了镜子");
                }
            }
        }
    }
    public void run(){
        try {
            makeUp();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        makeUp m1 = new makeUp(0, "灰姑娘");
        makeUp m2 = new makeUp(1, "白雪公主");
        m1.start();
        m2.start();
    }
}

结果如下:
在这里插入图片描述
分析:
代码中含有两个线程,分别是“灰姑娘”和“白雪公主”,在测试类中定义了两种共享资源lipsTick和mirror,static(对象内存地址不变)保证两种资源只有一份.

synchronized (mirror) 让“灰姑娘"独占mirror这个资源,之后利用sleep对该线程进行阻塞,之后更换了线程“白雪公主”,同样的,"白雪公主"利用synchronized (lipsTick)独占了资源lipsTick,再次利用sleep阻塞目前线程,之后cpu调度再次进入了"灰姑娘"的线程,线程继续执行嵌套的 synchronized (lipsTick)语句,“灰姑娘”想要获得lipsTick资源,但是该资源已经被“白雪公主”所在线程的资源上锁独占,这就形成了“灰姑娘”独占mirror资源,“白雪公主”独占lipsTick资源,而双方又想要获得对方的资源,从而程序一直处于等待状态,即所谓的死锁.

死锁图示:
在这里插入图片描述

如何预防死锁?

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

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

修改之后的代码:

package com.company;
//镜子mirror
//口红lipstick

class LipsTick {
}

class Mirror {
}

class makeUp extends Thread {
    static LipsTick lipsTick = new LipsTick();//static保证只有一份共享资源,被该类所有的实例共享
    static Mirror mirror = new Mirror(); //内存地址是不变的,就算重新建立该类的对象,但是该对象的地址不变
    int id;
    String name;

    makeUp(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public void makeUp() throws InterruptedException {
        if (this.id == 0) {
            synchronized (mirror) {         //获得了镜子,独占该镜子资源
                System.out.println(this.name + "获得了镜子");
                Thread.sleep(1000);      //阻塞该线程,让另一线程进入,不会释放锁

            }
            synchronized (lipsTick) {  //还想要口红
                System.out.println(this.name + "获得了口红");
            }
        } else {
            synchronized (lipsTick) { //进入第二条线程,获得口红,独占该口红资源
                System.out.println(this.name + "获得了口红");
                Thread.sleep(1000);   //阻塞该线程,让另一线程进入
            }
            synchronized (mirror) {
                System.out.println(this.name + "获得了镜子");
            }
        }
    }

    public void run() {
        try {
            makeUp();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        makeUp m1 = new makeUp(0, "灰姑娘");
        makeUp m2 = new makeUp(1, "白雪公主");
        m1.start();
        m2.start();
    }
}

运行结果:
在这里插入图片描述
可以看出,程序正常执行了.在没有修改之前,线程1没有释放锁,因为同步块中嵌套了一个同步块,而外层的同步块没有结束,锁1无法被释放.修改之后,将各个同步块分离,不形成嵌套,所以预防了死锁形成的条件2,所以死锁没有形成.

举报

相关推荐

0 条评论