• 关于java中死锁的总结


    关于死锁,估计很多程序员都碰到过,并且有时候这种情况出现之后的问题也不是非常好排查,下面整理的就是自己对死锁的认识,以及通过一个简单的例子来来接死锁的发生,自己是做python开发的,但是对于死锁的理解一直是一种模糊的概念,也是想过这次的整理更加清晰的认识这个概念。


    用来理解的例子是一个简单的生产者和消费者模型,这里是有一个生产者,有两个消费者,并且注意代码中使用notify方法的代码行

    package study_java.ex11;
    
    import java.util.LinkedList;
    import java.util.List;
    
    public class PCDemo1 {
        public static void main(String[] args){
            Pool pool = new Pool();
            Producter p1 = new Producter(pool);
            p1.setName("p1");
            Consumer c1 = new Consumer(pool);
            Consumer c2 = new Consumer(pool);
            c1.setName("c1");
            c2.setName("c2");
            p1.start();
            c1.start();
            c2.start();
        }
    }
    
    class Pool{
        private List<Integer> list = new LinkedList<Integer>();
        private int Max = 1;
        public void addLast(int n){
            String name = Thread.currentThread().getName();
            synchronized (this){
                while (list.size() >= Max){
                    try{
                        System.out.println(name+".wait()");
                        this.wait();
                    }
                    catch (Exception e){
                        e.printStackTrace();
                    }
                }
                System.out.println(name + "+" + n);
                list.add(new Integer(n));
                System.out.println(name + ".notify()");
                this.notify();   // 注意这里是调用的是notify方法
            }
        }
        public int remove(){
            String name = Thread.currentThread().getName();
            synchronized (this){
                while (list.size() == 0){
                    try{
                        System.out.println(name + ".wait()");
                        this.wait();
                    }
                    catch (Exception e){
                        e.printStackTrace();
                    }
    
                }
                System.out.println(name + "-" + 0);
                int no = list.remove(0);
                System.out.println(name + ".notify()");
                this.notify();  // 注意这里是调用的是notify方法
                return no;
            }
        }
    
    }
    
    // 生产者
    class Producter extends Thread{
        private Pool pool;
        static int i = 1;
        public Producter(Pool pool){
            this.pool = pool;
        }
        public void run(){
            while (true){
                pool.addLast(i++);
                System.out.println("生产者生产了"+i+"号");
            }
        }
    
    }
    
    
    // 消费者
    class Consumer extends Thread{
        private Pool pool;
        public Consumer(Pool pool){
            this.pool = pool;
        }
        public void run(){
            while (true){
                int no = pool.remove();
                System.out.println("消费者消费了"+no+"号");
            }
        }
    
    }

    这段代码的运行效果是日志,在最后程序卡主不动了:

    c1.wait()
    p1+1
    p1.notify()
    c1-0
    c1.notify()
    消费者消费了1号
    c1.wait()
    生产者生产了2号
    p1+2
    p1.notify()
    c1-0
    c1.notify()
    消费者消费了2号
    c1.wait()
    生产者生产了3号
    p1+3
    p1.notify()
    c1-0
    c1.notify()
    消费者消费了3号
    c1.wait()
    生产者生产了4号
    p1+4
    p1.notify()
    c1-0
    c1.notify()
    消费者消费了4号
    c1.wait()
    生产者生产了5号
    p1+5
    p1.notify()
    c1-0
    c1.notify()
    消费者消费了5号
    c1.wait()
    生产者生产了6号
    p1+6
    p1.notify()
    生产者生产了7号
    c1-0
    c1.notify()
    消费者消费了6号
    c1.wait()
    p1+7
    p1.notify()
    生产者生产了8号
    p1.wait()
    c2-0
    c2.notify()
    消费者消费了7号
    c2.wait()
    c1.wait()
    p1+8
    p1.notify()
    生产者生产了9号
    p1.wait()
    c2-0
    c2.notify()
    消费者消费了8号
    c2.wait()
    c1.wait()

    对上面的出现卡主的情况进行分析,理解为啥会卡主:

    从这次的执行效果可以看出第一次是c1抢到了执行权,但是这个时候pool是空
    所以c1没有可以消费的对象,被放入到了等待队列

    接着p1抢到了执行权,生产了1个,然后p1.notify(),这个时候等待队列里只有c1,所以c1被唤醒,c1消费了1个,然后c1.notify(), 这个时候等待队列也没有等待的,这个时候有被c1抢到了执行权,但是pool里没有可以消费的内容,所以c1.wait() 进入到等待队列

    这个时候p1抢到执行权,生产了1个,然后p1.notify(),这个时候等待队列里只有c1,所以c1被唤醒,c1也抢到了执行权,消费了1个,然后c1.notify()
    同样这个时候等待队列里没有等待的,c1这次又抢到了执行权,但pool里没有可以消费的内容,所以c1.wait(),进入到等待队列

    p1 又抢到了执行权,生产1个,然后p1.notify(),这个时候等待队列里只有c1,所以c1被唤醒,c1也抢到了执行权,消费了1个,然后c1.notify()
    同样这个时候等待队列里没有等待的,c1这次又抢到了执行权,但pool里没有可以消费的内容,所以c1.wait(),进入到等待队列

    .......这种情况重复了几次

    但是运行到下面这段的时候问题出现了:

    p1+7
    p1.notify()
    生产者生产了8号
    p1.wait()
    c2-0
    c2.notify()
    消费者消费了7号
    c2.wait()
    c1.wait()
    p1+8
    p1.notify()
    生产者生产了9号
    p1.wait()
    c2-0
    c2.notify()
    消费者消费了8号
    c2.wait()
    c1.wait()

    继续进行分析,中间重复的部分不做分析了,和前面的过程是一样的

    这个时候等待队里里依然是c1 这个时候p1抢到了执行权,生产了1个,p1.notify() 这个时候等待队列里只有c1,所以c1被唤醒,但是c1没有抢过p1,p1自己又抢到了执行权,但是这个时候pool里面已经有内容,所以p1没有生产,p1.wait(),p1进入等待队列

    这个时候c2抢到了执行权,c2消费1个,c2.notify() 这个时候等待队里是p1,p1被唤醒,但是这个时候c2抢到了执行权,但是pool没有内容可以消费所以c2.wait() 进入等待队列

    接着c1抢到了执行权,同样pool没有可以消费的内容,c1.wait() 进入到等待队列

    p1这个时候抢到了执行权,p1生产了1个,接着p1.notify() 这个时候等待队列里有c1和c2,但是只有一个会被唤醒,不管是哪个,结果没抢过p1,p1再次拿到执行权,但是这个时候pool已经有内容,所以p1.wait() p1进入等待队列

    从下面是c2执行,可以看出刚才是c2被唤醒了,这个时候c2也拿到了执行权消费了1个。c2.notify() 等待队列里这个时候有c1 和p1 但是这个时候c2 自己抢到了执行权,但是没有可以消费的,c2.wait() c2 进入等待队列
    不巧的是刚才抢到执行权的正好是c1,所以c1继续wait,再次进入等待队列

    到这个时候p1 c1 c2 都进入等待队列里,都在等待唤醒,也就出现了程勋最后卡住不动的情况

    解决的方法有两种:

    第一种:
    其实解决上面的方法也比较简单,就是把调用notify的地方全部换成notifyAll方法

    notify和notifyAll的区别是,当执行notifyAll的时候会唤醒所有等待的线程,从而避免之前的都在等待队列等待的问题

    第二种:
    就是wait()的时候加上超时参数,不是像之前一直傻等,而是在超过既定的时间之后自己唤醒

  • 相关阅读:
    CodeForces
    codeforces 1250B The Feast and the Bus
    CF1038D Slime
    CodeForces-208C Police Station
    差分约束
    HDU 2586
    HDU 3948
    HDU 2222
    作业
    闰年的判断
  • 原文地址:https://www.cnblogs.com/zhaof/p/9291290.html
Copyright © 2020-2023  润新知