深入多线程十二:什么是死锁?(场景+代码示例)

在学习Java的道路上,是否路过多线程时总让你很迷惘;很不巧,我也是,而使我们感到很迷惘主要原因都源于没有对概念的深深的理解和实践。所以我决定漫步Java多线程,同你一起会会多线程。

深入多线程系列

深入多线程一:理解多线程在于深深地理解了多任务、进程、多线程、线程
深入多线程二:手撕多线程,从会三种创建多线程方式开始:除了常见的两种,你是否了解Callable接口方式?
深入多线程三:初遇并发问题:从一个小故事开始,从一行行代码开始
深入多线程四:停止多线程,你不会还以为是用stop和destroy吧?
深入多线程五:多线程为何要使用休眠?
深入多线程六:线程礼让与强制执行
深入多线程七:纯手绘图解多线程状态+代码示例,就问你怕了吗?
深入多线程八:多线程的优先级
深入多线程九:守护线程
深入多线程十:通过案例体会多线程的不安全
深入多线程十一: 超全synchronized的两种用法:同步方法与同步块
深入多线程十二:什么是死锁?
深入多线程十三:什么是Lock锁,比起synchronized锁有什么区别?
深入多线程十四:经典生产者与消费者问题,本质是线程通信问题
深入多线程十五:管程方式解决生产者与消费者问题
深入多线程十六:信号灯方式解决生产者与消费者问题
深入多线程十七:什么是池?线程池方式解决生产者与消费者问题

1.死锁概念

多线程因竞争资源而导致两个或者多个线程僵持(相互等待),停止执行的现象。

当一个同步块同时拥有两个以上对象的锁,就可能会发生"死锁"问题。

2.故事场景

从那遥远的地方,

有两头羊要过窄窄的独木桥,分别有白羊和黑羊,

它们各自都要前往对面桥一方,

可是窄窄的桥同时只能过去一只,

所以彼此僵持…

直到狭路相逢勇者胜!

直到退一步海阔天空!

在这里插入图片描述

3.代码示例

3.1主方法执行

public class DemoThread {
    public static void main(String[] args) {
        new Thread(new Bridge(0,"白羊")).start();
        new Thread(new Bridge(1,"黑羊")).start();
    }
}

3.2创建白羊和黑羊类

class WhiteSheep{}

class BlackSheep{}

3.3创建桥类

class Bridge extends Thread{

    private Integer choice;
    private String name;

    static WhiteSheep whiteSheep = new WhiteSheep();
    static BlackSheep blackSheep = new BlackSheep();

    Bridge(Integer choice , String name){
        this.choice = choice;
        this.name = name;
    }


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

    private void Bridge() throws InterruptedException {
        if (choice == 0){
            synchronized (whiteSheep){
                System.out.println(this.name + "前进===1");
                Thread.sleep(2000);
                synchronized (blackSheep){
                    System.out.println(this.name + "前进===2");
                }
            }

        }else {
            synchronized ( blackSheep){
                System.out.println(this.name + "前进===3");
                Thread.sleep(3000);
                synchronized (whiteSheep){
                    System.out.println(this.name + "后退===4");
                }
            }
        }
    }
}

运行结果
死锁情况下,线程会彼此僵持

在这里插入图片描述

将同步块抽离出来,让每一个同步块独立

class Bridge extends Thread{

    private Integer choice;
    private String name;

    static WhiteSheep whiteSheep = new WhiteSheep();
    static BlackSheep blackSheep = new BlackSheep();

    Bridge(Integer choice , String name){
        this.choice = choice;
        this.name = name;
    }
    
    @Override
    public void run() {
        try {
            Bridge();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void Bridge() throws InterruptedException {
        if (choice == 0){
            synchronized (whiteSheep){
                System.out.println(this.name + "前进===1");
                Thread.sleep(2000);
            }
            synchronized (blackSheep){
                System.out.println(this.name + "前进===2");
            }
        }else {
            synchronized ( blackSheep){
                System.out.println(this.name + "前进===3");
                Thread.sleep(3000);
            }
            synchronized (whiteSheep){
                System.out.println(this.name + "后退===4");
            }
        }
    }
}

在这里插入图片描述

死锁产生4个必要条件:

1.互斥条件 一个资源每次只能被一个进程使用。
2.请求与保持条件 当进程因请求资源而阻塞时,对已获得的资源保持不放
3.不剥削条件 进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放
4.环路等待条件 在发生死锁时,必然存在一个进程–资源的环形链

死锁产生4个必要条件,只需要解决一个或多个条件就可以避免死锁现象。

最后:

最后的最后,为了更好的阅读体验,我把想说的话都放在了下面,嘿嘿。

我是一颗剽悍的种子 把我会的,认真的分享 是我写博客一直不变的信条。
如果你能看到这篇博文,说明咱们还是很有缘的;希望能带给你一些许帮助,创作的不易,
把我文章的知识带走,你的三连留下,点赞,评论,关注,是我最大的动力。

猜你喜欢

转载自blog.csdn.net/A_hxy/article/details/108152076