java基础------线程之死锁处理方法

前言

死锁不是一种正常的现象, 是在多线程程序中需要避免的

定义

多个进程在执行过程中,因争夺同类资源且资源分配不当而造成的一种互相等待的现象,若无外力作用,它们都将永远无法继续执行,这种状态称为死锁,这些处于等待状态的进程称为死锁进程。

死锁代码

也许大家现在看到死锁的定义还不是很清楚很懵死锁到底是什么,接下来带大家通过代码来了解死锁,重点都在代码中体现,都标注的很清晰,认真看过才会有收获。

ChopStick类

package 线程.死锁问题;
public class ChopStick {
    //代表筷子的编号
    private String num;//1号筷子
    //构建chopstack方法
    public ChopStick(String num){
        this.num=num;
    }
    //提供公有方法,get方法方便对筷子属性进行使用
    public String getNum() {
        return num;
    }
}

ZhuJia类

package 线程.死锁问题;

public class ZhuJia extends Thread{
    private ChopStick left;//左筷子
    private ChopStick right;//右筷子
    private String name;//使用者
    //创建构造方法
    public ZhuJia(ChopStick left,ChopStick right,String name){
        this.left=left;
        this.right=right;
        this.name=name;
    }
    public void run(){
        synchronized (left){
        System.out.println(this.name+"拿起左侧筷子"+left.getNum()+"筷子"+"准备拿右侧"+right.getNum()+"筷子");
        synchronized (right){
            System.out.println(this.name+"拿起右侧筷子"+right.getNum()+"筷子正吃饭");
        }
        }
    }
}

测试类

package 线程.死锁问题;

public class Test {
    public static void main(String[] args) {
        //构建筷子编号
        ChopStick c_1=new ChopStick("1号");
        ChopStick c_2=new ChopStick("2号");
        ChopStick c_3=new ChopStick("3号");
        ChopStick c_4=new ChopStick("4号");
        ChopStick c_5=new ChopStick("5号");
        //构建五个哲学家线程
        ZhuJia z_1 = new ZhuJia(c_1, c_5, "李彦宏");
        ZhuJia z_2 = new ZhuJia(c_2, c_1, "马云");
        ZhuJia z_3 = new ZhuJia(c_3, c_2, "马化腾");
        ZhuJia z_4 = new ZhuJia(c_4, c_3, "任正非");
        ZhuJia z_5 = new ZhuJia(c_5, c_4, "柳传志");
        //启动线程
        z_1.start();
        z_2.start();
        z_3.start();
        z_4.start();
        z_5.start()
    }
}

结果输出有来两种清况

正常效果如下

李彦宏拿起左侧筷子1号筷子准备拿右侧5号筷子
李彦宏拿起右侧筷子5号筷子正吃饭
马云拿起左侧筷子2号筷子准备拿右侧1号筷子
柳传志拿起左侧筷子5号筷子准备拿右侧4号筷子
马云拿起右侧筷子1号筷子正吃饭
马化腾拿起左侧筷子3号筷子准备拿右侧2号筷子
马化腾拿起右侧筷子2号筷子正吃饭
柳传志拿起右侧筷子4号筷子正吃饭
任正非拿起左侧筷子4号筷子准备拿右侧3号筷子
任正非拿起右侧筷子3号筷子正吃饭    

死锁效果如下

多个进程在执行过程中,因争夺同类资源且资源分配不当而造成的一种互相等待的现象,大白话就是谁都不想让就造成这种死锁现象

马云拿起左侧筷子2号筷子准备拿右侧1号筷子
柳传志拿起左侧筷子5号筷子准备拿右侧4号筷子
任正非拿起左侧筷子4号筷子准备拿右侧3号筷子
李彦宏拿起左侧筷子1号筷子准备拿右侧5号筷子
马化腾拿起左侧筷子3号筷子准备拿右侧2号筷子

解决方案:

1.让有筷子交差的科学家,设立时间差
2.避免锁的嵌套
3.设置标志位|

方案一 设立时间差

ChopStick类

package 线程.死锁解决方案一;

public class ChopStick {
    //代表筷子的编号
    private String num;//1号筷子
    //构建chopstack方法
    public ChopStick(String num){
        this.num=num;
    }
    //提供公有方法,get方法方便对筷子属性进行使用
    public String getNum() {
        return num;
    }
}

ZhuJia类

package 线程.死锁解决方案一;

public class ZhuJia extends Thread{
    /*
    *  1.让有筷子交差的科学家,设立时间差
    * 可以理解成交通信号灯,按顺序行驶
    * */
    private ChopStick left;//左筷子
    private ChopStick right;//右筷子
    private String name;//使用者
    private  int time;//设立时间差
    //创建构造方法
    public ZhuJia(ChopStick left, ChopStick right, String name,int time){
        this.left=left;
        this.right=right;
        this.name=name;
        this.time=time;
    }
    public void run(){
        synchronized (left){
        System.out.println(this.name+"拿起左侧筷子"+left.getNum()+"筷子"+"准备拿右侧"+right.getNum()+"筷子");
        synchronized (right){
            System.out.println(this.name+"拿起右侧筷子"+right.getNum()+"筷子正吃饭");
        }
        }
    }
}

测试类

package 线程.死锁解决方案一;

/**
 * @author fengxun
 * @version 1.0
 * @date 2020/4/25 15:41
 */
public class Test {
    public static void main(String[] args) {
        //构建筷子编号
        ChopStick c_1=new ChopStick("1号");
        ChopStick c_2=new ChopStick("2号");
        ChopStick c_3=new ChopStick("3号");
        ChopStick c_4=new ChopStick("4号");
        ChopStick c_5=new ChopStick("5号");
        //构建五个哲学家线程
        ZhuJia z_1 = new ZhuJia(c_1, c_5, "李彦宏",100);
        //时间差数值根据两者不相邻可以相同,反之不相同
        ZhuJia z_2 = new ZhuJia(c_2, c_1, "马云",200);
        ZhuJia z_3 = new ZhuJia(c_3, c_2, "马化腾",300);
        ZhuJia z_4 = new ZhuJia(c_4, c_3, "任正非",200);
        ZhuJia z_5 = new ZhuJia(c_5, c_4, "柳传志",300);
        //启动线程
        z_1.start();
        z_2.start();
        z_3.start();
        z_4.start();
        z_5.start();
        //每个人都吃得了饭
/*      李彦宏拿起左侧筷子1号筷子准备拿右侧5号筷子
        李彦宏拿起右侧筷子5号筷子正吃饭
        马云拿起左侧筷子2号筷子准备拿右侧1号筷子
        柳传志拿起左侧筷子5号筷子准备拿右侧4号筷子
        马云拿起右侧筷子1号筷子正吃饭
        马化腾拿起左侧筷子3号筷子准备拿右侧2号筷子
        马化腾拿起右侧筷子2号筷子正吃饭
        柳传志拿起右侧筷子4号筷子正吃饭
        任正非拿起左侧筷子4号筷子准备拿右侧3号筷子
        任正非拿起右侧筷子3号筷子正吃饭*/

        //就不会有一下这种清况每个人都不愿意放下手中的筷子,到最后都没吃上

/*      马云拿起左侧筷子2号筷子准备拿右侧1号筷子
        柳传志拿起左侧筷子5号筷子准备拿右侧4号筷子
        任正非拿起左侧筷子4号筷子准备拿右侧3号筷子
        李彦宏拿起左侧筷子1号筷子准备拿右侧5号筷子
        马化腾拿起左侧筷子3号筷子准备拿右侧2号筷子
        */

    }
}

方案二 避免锁的嵌套

ChopStick类

package 线程.死锁解决方案二;

public class ChopStick {
    //代表筷子的编号
    private String num;//1号筷子
    //构建chopstack方法
    public ChopStick(String num){
        this.num=num;
    }
    //提供公有方法,get方法方便对筷子属性进行使用
    public String getNum() {
        return num;
    }
}

ZhuJia类

package 线程.死锁解决方案二;

public class ZhuJia extends Thread{
    /*
    * 2.避免锁的嵌套
    * 可以把它想成嵌套for循环,外层执行一次,内层执行好多次直到结束才返回外层,就好比外层人员都拿着一支筷子,谁都不愿意放手,而内层循环走不出去就造成死锁现象
    * */
    private ChopStick left;//左筷子
    private ChopStick right;//右筷子
    private String name;//使用者
    //创建构造方法
    public ZhuJia(ChopStick left, ChopStick right, String name){
        this.left=left;
        this.right=right;
        this.name=name;
    }
    public void run(){
        //不嵌套得话当拿不到筷子时就回放下,这样就会使得每个都会吃上饭
        synchronized (left) {
            System.out.println(this.name + "拿起左侧筷子" + left.getNum() + "筷子" + "准备拿右侧" + right.getNum() + "筷子");
        }
        synchronized (right){
            System.out.println(this.name+"拿起右侧筷子"+right.getNum()+"筷子正吃饭");

        }
    }
}

测试类

package 线程.死锁解决方案二;

public class Test {
    public static void main(String[] args) {
        //构建筷子编号
        ChopStick c_1=new ChopStick("1号");
        ChopStick c_2=new ChopStick("2号");
        ChopStick c_3=new ChopStick("3号");
        ChopStick c_4=new ChopStick("4号");
        ChopStick c_5=new ChopStick("5号");
        //构建五个哲学家线程
        ZhuJia z_1 = new ZhuJia(c_1, c_5, "李彦宏");
        ZhuJia z_2 = new ZhuJia(c_2, c_1, "马云");
        ZhuJia z_3 = new ZhuJia(c_3, c_2, "马化腾");
        ZhuJia z_4 = new ZhuJia(c_4, c_3, "任正非");
        ZhuJia z_5 = new ZhuJia(c_5, c_4, "柳传志");
        //启动线程
        z_1.start();
        z_2.start();
        z_3.start();
        z_4.start();
        z_5.start();
        
        //每个人都吃了饭
/*      李彦宏拿起左侧筷子1号筷子准备拿右侧5号筷子
        李彦宏拿起右侧筷子5号筷子正吃饭
        马云拿起左侧筷子2号筷子准备拿右侧1号筷子
        柳传志拿起左侧筷子5号筷子准备拿右侧4号筷子
        马云拿起右侧筷子1号筷子正吃饭
        马化腾拿起左侧筷子3号筷子准备拿右侧2号筷子
        马化腾拿起右侧筷子2号筷子正吃饭
        柳传志拿起右侧筷子4号筷子正吃饭
        任正非拿起左侧筷子4号筷子准备拿右侧3号筷子
        任正非拿起右侧筷子3号筷子正吃饭*/
        
        //先这种清况每个人都不愿意放下手中的筷子

/*      马云拿起左侧筷子2号筷子准备拿右侧1号筷子
        柳传志拿起左侧筷子5号筷子准备拿右侧4号筷子
        任正非拿起左侧筷子4号筷子准备拿右侧3号筷子
        李彦宏拿起左侧筷子1号筷子准备拿右侧5号筷子
        马化腾拿起左侧筷子3号筷子准备拿右侧2号筷子
        */

    }
}

方案三 设置标志位

ChopStick类

package 线程.死锁解决方案三;

public class ChopStick {
    //代表筷子的编号
    private String num;//1号筷子
    //判断筷子是否被占用
    private boolean free = true;//筷子没有被占用
    //构建chopstack方法
    public ChopStick(String num){
        this.num=num;
    }
    //提供公有方法,get方法方便对筷子属性进行使用
    public String getNum() {
        return num;
    }
    //拿起筷子
    public boolean Takeup() {
        //判断筷子是否占用   true(没有占用)
        if (free) {
            free = false;//表示占用
            return true;//代表占用
        } else {
            return false;//代表没有占用
        }

    }
    //放下筷子
    public void  Putdome(){
        free=true;
    }
}

ZhuJia类

package 线程.死锁解决方案三;

/**
 * @author fengxun
 * @version 1.0
 * @date 2020/4/25 15:32
 */
public class ZhuJia extends Thread{
    private ChopStick left;//左筷子
    private ChopStick right;//右筷子
    private String name;//使用者
    //创建构造方法
    public ZhuJia(ChopStick left, ChopStick right, String name){
        this.left=left;
        this.right=right;
        this.name=name;
    }
    public void run(){
/*        synchronized (left){
        System.out.println(this.name+"拿起左侧筷子"+left.getNum()+"筷子"+"准备拿右侧"+right.getNum()+"筷子");
        synchronized (right){
            System.out.println(this.name+"拿起右侧筷子"+right.getNum()+"筷子正吃饭");
        }
        }*/
        while (true) {
            //拿到左筷子
            if (left.Takeup()) {//true
                System.out.println(this.name + "拿起左侧筷子" + left.getNum() + "筷子" + "准备拿右侧" + right.getNum() + "筷子");
                if (right.Takeup()) {
                    System.out.println(this.name + "拿起右侧筷子" + right.getNum() + "筷子正吃饭");
                    //给人吃饭预留些时间,不可能刚拿起来筷子就吃完了,
                    try {
                        Thread.sleep(500);//给他500毫秒时间让他吃饭
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("放下左筷子" + left.getNum() + "放下右筷子" + right.getNum());//吃完饭放下筷子让其他人吃饭
                    left.Putdome();//左筷子放下
                    right.Putdome();//右筷子放下
                } else {
                    //拿到左筷子了,但是遗憾右筷子没有拿到,需要犯下左筷子
                    left.Putdome();
                }
            }
                //还有一种是左筷子没有拿到,让他等一等,喝些水再吃饭
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        }
    }
}

测试类

package 线程.死锁解决方案三;

public class Test {
    public static void main(String[] args) {
        //构建筷子编号
        ChopStick c_1=new ChopStick("1号");
        ChopStick c_2=new ChopStick("2号");
        ChopStick c_3=new ChopStick("3号");
        ChopStick c_4=new ChopStick("4号");
        ChopStick c_5=new ChopStick("5号");
        //构建五个哲学家线程
        ZhuJia z_1 = new ZhuJia(c_1, c_5, "李彦宏");
        ZhuJia z_2 = new ZhuJia(c_2, c_1, "马云");
        ZhuJia z_3 = new ZhuJia(c_3, c_2, "马化腾");
        ZhuJia z_4 = new ZhuJia(c_4, c_3, "任正非");
        ZhuJia z_5 = new ZhuJia(c_5, c_4, "柳传志");
        //启动线程
        z_1.start();
        z_2.start();
        z_3.start();
        z_4.start();
        z_5.start();
        
        //通过以下结果可知每位客人都吃上饭
        /*
        *       李彦宏拿起左侧筷子1号筷子准备拿右侧5号筷子
                任正非拿起左侧筷子4号筷子准备拿右侧3号筷子
                柳传志拿起左侧筷子5号筷子准备拿右侧4号筷子
                马化腾拿起左侧筷子3号筷子准备拿右侧2号筷子
                马云拿起左侧筷子2号筷子准备拿右侧1号筷子
                马云拿起右侧筷子1号筷子正吃饭
                柳传志拿起右侧筷子4号筷子正吃饭
                马化腾拿起左侧筷子3号筷子准备拿右侧2号筷子
                放下左筷子5号放下右筷子4号
                放下左筷子2号放下右筷子1号
                马云拿起左侧筷子2号筷子准备拿右侧1号筷子
                马化腾拿起左侧筷子3号筷子准备拿右侧2号筷子
                马化腾拿起右侧筷子2号筷子正吃饭
                李彦宏拿起左侧筷子1号筷子准备拿右侧5号筷子
                柳传志拿起左侧筷子5号筷子准备拿右侧4号筷子
                任正非拿起左侧筷子4号筷子准备拿右侧3号筷子
                柳传志拿起左侧筷子5号筷子准备拿右侧4号筷子
                任正非拿起左侧筷子4号筷子准备拿右侧3号筷子
                放下左筷子3号放下右筷子2号
                李彦宏拿起左侧筷子1号筷子准备拿右侧5号筷子
                李彦宏拿起右侧筷子5号筷子正吃饭
        * */
    }
}

案例(面试提问)

案例描述:

现在有张三想要李四的画,李四想要张三的书。
张三对李四说了:“你给我画,我就把书给你”。
李四也对张三说了:“你给我书,我就把画给你”。
此时,张三在等着李四的答复,而李四也在等着张三的答复,那么这样下去的最终结果就是张三得不到李四的画,李四也得不到张三的书。这实际上就是死锁的概念。

代码如下:

LISi类

package 线程.互要书画;

public class LISi {
    //说话方法
    public void say(){
        System.out.println("李四对李四张三说你把画给我,我就把书给你");
    }
    //获得的方法
    public void get(){
        System.out.println("李四得到张三的画");

    }
}

ZhangSan类

package 线程.互要书画;

public class ZhangSan {
    //说话方法
    public void say(){
        System.out.println("张三对李四说你把书给我,我就把画给你");
    }
    //获得的方法
    public void get(){
        System.out.println("张三得到李四的书");
    }
}

MyThread类

package 线程.互要书画;

public class MyThread extends Thread {
    //标识
    boolean flag=true;//为true张三先开口
    //声明张三李四注意用static修饰,保证张三李四唯一性(只开辟一处空间,要不然写两次)
    public static ZhangSan zhangSan=new ZhangSan();
    public static LISi liSi=new LISi();
    @Override
    public void run(){
        if (flag) {
            zhangSan.say();
            zhangSan.get();
        }else {
            liSi.say();
            liSi.get();
        }
    }
}

测试类

package 线程.互要书画;

public class Test {
    public static void main(String[] args) {
        //创建张三李四线程
        MyThread myThread=new MyThread();
        MyThread myThread1=new MyThread();
        //
        myThread.flag=true;//张三先开口
        myThread1.flag=false;

        //启动线程
        myThread.start();
        myThread1.start();
        
        //实现了张三向李四借书,李四向张三借画过程
       /*   张三对李四说你把书给我,我就把画给你
            李四对李四张三说你把画给我,我就把书给你
            张三得到李四的书
            李四得到张三的画*/
    }
}

总结

1、注意

死锁不是一种正常的现象, 是在多线程程序中需要避免的

2、产生死锁的原因主要是

因为系统资源不足。
进程运行推进的顺序不合适。
资源分配不当等。
如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则
就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁

3、产生死锁的必要条件

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

想要代码可以到我资源处下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值