天天看点

关于对死锁的理解

文章目录

    • 什么是死锁?
    • 举例理解死锁
    • 如何解决死锁
    • 锁池与等待池的理解
      • 什么是锁池与等待池
      • 锁池与等待池的区别
    • 两个线程交替执行

什么是死锁?

所谓死锁,是指互斥锁标记使用不当造成多个线程,相互持有对方想要申请的资源,不释放的情况下又去主动申请对方已经持有的资源从而双双进入对方持有资源的锁池当中 产生永久的阻塞 - 死锁 DeadLock。

举例理解死锁

中美科学家联合国饿死事件

中国科学家拿着刀叉想吃面条

美国科学家拿着筷子想吃面条

泉城路奔宝事件

奔驰占领泉城路东侧 想要开往泉城路西侧

宝马占领泉城路西侧 想要开往泉城路东侧

关于对死锁的理解

下面以泉城路奔宝事件举例说明

public class TestDeadLock{
    public static void main(String[] args){
        QCRoad r = new QCRoad();
        QCRoad.Benz s900 = r.new Benz();
        QCRoad.Bmw x9 = r.new Bmw();
        s900.start();
        x9.start();
    }
}
class QCRoad{//餐厅
    Object east = new Object();//路东资源   刀叉
    Object west = new Object();//路西资源    筷子

    class Benz extends Thread{//中国人
        @Override
        public void run(){
            System.out.println("勋总驾驶奔驰驶出家门 去上课~");
            synchronized(east){
                System.out.println("勋总和他的奔驰已经占领了泉城路东侧~");
                synchronized(west){
                    System.out.println("勋总和他的奔驰又占领了泉城路西侧~");
                }
            }

            System.out.println("勋总顺利的通过了泉城路");
        }
    }
    class Bmw extends Thread{//美国人
        @Override
        public void run(){
            System.out.println("良总驾驶宝马驶出家门 去上课~");
            synchronized(west){
                System.out.println("良总和他的宝马已经占领了泉城路西侧~");
                synchronized(east){
                    System.out.println("良总和他的宝马又占领了泉城路东侧~");
                    east.notify();
                }
            }
            System.out.println("良总顺利的通过了泉城路");
        }
    }
}
           

奔驰占领泉城路东侧宝马占领泉城路西侧无法通行,从而产生死锁

关于对死锁的理解

多运行几次发现通过了

关于对死锁的理解

怎么回事?

宝马去晚了 没堵住~

为了演示死锁让奔驰占领路懂后不要往西开,宝马占领路西后不要往东开 睡一会~

public class TestDeadLock{
    public static void main(String[] args){
        QCRoad r = new QCRoad();
        QCRoad.Benz s900 = r.new Benz();
        QCRoad.Bmw x9 = r.new Bmw();
        s900.start();
        x9.start();
    }
}
class QCRoad{//餐厅
    Object east = new Object();//路东资源   刀叉
    Object west = new Object();//路西资源    筷子

    class Benz extends Thread{//中国人
        @Override
        public void run(){
            System.out.println("勋总驾驶奔驰驶出家门 去上课~");
            synchronized(east){
                System.out.println("勋总和他的奔驰已经占领了泉城路东侧~");
                try{sleep(100);}catch(Exception e){e.printStackTrace();}
                synchronized(west){
                    System.out.println("勋总和他的奔驰又占领了泉城路西侧~");
                }
            }
            System.out.println("勋总顺利的通过了泉城路");
        }
    }
    class Bmw extends Thread{//美国人
        @Override
        public void run(){
            System.out.println("良总驾驶宝马驶出家门 去上课~");
            synchronized(west){
                System.out.println("良总和他的宝马已经占领了泉城路西侧~");
                try{sleep(100);}catch(Exception e){e.printStackTrace();}
                synchronized(east){
                    System.out.println("良总和他的宝马又占领了泉城路东侧~");
                }
            }
            System.out.println("良总顺利的通过了泉城路");
        }
    }

}
           

如何解决死锁

一块空间

对象的等待池

关于对死锁的理解

三个方法

  • wait() : 让当前线程放弃已经持有的锁标记 并且进入调用方法的那个对象的等待池当中
  • notify() : 从调用方法的那个对象的等待池当中随机的唤醒一个线程
  • notifyAll() : 从调用方法的那个对象的等待池当中唤醒所有阻塞的线程

这三个方法都是Object类的方法 所以 任何一个对象 都可以使用

因为任何一个对象都有等待池

这三个方法都必须在已经持有锁标记的前提下才能使用 否则 不但是调用失败

还会触发运行时异常~

如何使用

使用wait()让奔驰去等待池阻塞 宝马通过后再notify()让奔驰出来

public class TestDeadLock{
    public static void main(String[] args){
        QCRoad r = new QCRoad();
        QCRoad.Benz s900 = r.new Benz();
        QCRoad.Bmw x9 = r.new Bmw();
        s900.start();
        x9.start();
    }
}
class QCRoad{//餐厅
    Object east = new Object();//路东资源   刀叉
    Object west = new Object();//路西资源    筷子

    class Benz extends Thread{//中国人
        @Override
        public void run(){
            System.out.println("勋总驾驶奔驰驶出家门 去上课~");
            synchronized(east){
                System.out.println("勋总和他的奔驰已经占领了泉城路东侧~");
                try{sleep(100);}catch(Exception e){e.printStackTrace();}
                try{east.wait();}catch(Exception e){e.printStackTrace();}
                synchronized(west){
                    System.out.println("勋总和他的奔驰又占领了泉城路西侧~");
                }
            }

            System.out.println("勋总顺利的通过了泉城路");
        }
    }
    class Bmw extends Thread{//美国人
        @Override
        public void run(){
            System.out.println("良总驾驶宝马驶出家门 去上课~");
            synchronized(west){
                System.out.println("良总和他的宝马已经占领了泉城路西侧~");
                try{sleep(100);}catch(Exception e){e.printStackTrace();}
                synchronized(east){
                    System.out.println("良总和他的宝马又占领了泉城路东侧~");
                    east.notify();
                }
            }
            System.out.println("良总顺利的通过了泉城路");
        }
    }
}
           

锁池与等待池的理解

什么是锁池与等待池

锁池: 假设线程A已经拥有了某个对象(不是类)的锁,而其他线程B,C想要调用这个对象的某个synchronized方法(或者块),由于B,C线程在进入对象的synchronized方法(或者块)之前必须先获得该对象锁的拥有权,而恰巧该对象的锁目前正被线程A所占用,此时B,C线程会被阻塞,进入一个地方去等待锁的释放,这个地方就是该对象的锁池。

等待池: 假设线程A调用了某个对象的wait方法,线程A就会释放该对象的锁,同时线程A就进入到了该对象的等待池中,进入到等待池中的线程不会去竞争该对象的锁。

锁池与等待池的区别

锁池和等待池都是Java当中每个对象都有一份的 用来放置线程的空间

进入的时候是否需要释放资源

锁池: 不需要释放资源就能直接进入 (所以才会形成死锁)

等待池: 必须要先释放资源 才能进入

离开的时候是否需要调用方法

锁池: 不需要 一旦锁标记归还再度可用 锁池就可以解除

等待池: 必须要另外的线程主动唤醒 notify() / notifyAll()

离开之后去到什么状态

离开锁池: 前往就绪状态

离开等待池: 前往锁池! [等着唤醒我的线程释放锁标记]

两个线程交替执行

比如 顺丰 有些东西是不能上飞机的 只能陆运

顺丰就要安排大货车配合两个司机

public class BigOne{
    public static void main(String[] args){
        RightThread rt = new RightThread();
        LeftThread lt = new LeftThread(rt);

        lt.start();
        //rt.start();
    }
}
class X{
    static Object obj = new Object();
}
class LeftThread extends Thread{
    RightThread rt;
    public LeftThread(RightThread rt){
        this.rt = rt;
    }
    @Override
    public void run(){
        synchronized(X.obj){
            rt.start();
            for(int i = 0;i<6666;i++){
                System.out.println("左脚");
                try{X.obj.wait();}catch(Exception e){e.printStackTrace();}
                X.obj.notify();
            }
        }
    }
}
class RightThread extends Thread{
    @Override
    public void run(){
        synchronized(X.obj){
            for(int i = 0;i<6666;i++){
                System.out.println("            右脚");
                X.obj.notify();
                try{X.obj.wait();}catch(Exception e){e.printStackTrace();}
            }
        }
    }
}