关于死锁, 估计很多程序员都碰到过, 并且有时候这种情况出现之后的问题也不是非常好排查, 下面整理的就是自己对死锁的认识, 以及通过一个简单的例子来来接死锁的发生, 自己是做 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() 的时候加上超时参数, 不是像之前一直傻等, 而是在超过既定的时间之后自己唤醒
来源: https://www.cnblogs.com/zhaof/p/9291290.html