1.死锁是如何产生

 多个线程同时被阻塞

它们其中的一个或者多个等待某个资源的释放

而由于线程全部阻塞,资源的释放无限期的延迟

因此程序不可能终止

2.死锁的三个典型情况

2.1 一个线程一把锁,可重入锁没事,不可重入锁死锁

2.2 两个线程两把锁,即使是可重入锁,也会出现死锁

public class ThreadDemo {
    static class Add{
        public int count;
        public void add(int count) {
            count++;
        }
    }

    public static void main(String[] args) {
        Object blocker1 = new Object();
        Object blocker2 = new Object();
        Add a= new Add();
       // blocker2 释放后 blocker1 才解锁
        Thread t1 = new Thread(() -> {
            synchronized (blocker1) {
                synchronized (blocker2) {
                    for (int i = 0; i < 5000; i++) {
                        a.add(a.count);
                    }
                    System.out.println(a.count);
                }
            }
        });
         // blocker1 释放后 blocker2 才解锁 
        Thread t2 = new Thread(() -> {
            synchronized(blocker2) {
                synchronized (blocker1) {
                    for (int i = 0; i < 5000; i++) {
                        a.add(a.count);
                    }
                    System.out.println(a.count);
                }
            }
        });
    }
}

循环等待

浅谈死锁_i++

2.3 n 个线程, m 把锁(哲学家就餐问题)

每个哲学家两两之间放着一根筷子

浅谈死锁_重入锁_02

每个哲学家有么思考人生,要么拿起两根筷子吃面(先拿左再拿右)

浅谈死锁_死锁_03

浅谈死锁_死锁_04

假如哲学家同时拿起左手边筷子,如果再拿起右手边就会发现筷子都被占用了

哲学家互不相让就会出现死锁

3.死锁的四个必要条件

3.1 互斥使用

当资源被一个线程使用时,另外的线程不能使用

3.2 不可抢占

一个线程拿到锁只能自己主动释放锁,其他的线程不可强行占有

3.3 请求和保持

当资源请求者保持对原有的资源占用,同时在请求其他的资源(吃着着碗里的,看着锅里的)

3.4 循环等待

存在一个等待队列, q1占用q2, q2占用q1(家里钥匙落车里,车钥匙落家里)

4.如何打破死锁

任意打破死锁的四个必要条件之一

而循环等待是最容易打破的,我们可以把每个锁进行排序

锁排序

n个线程m个锁,针对m个锁从小到大进行锁排序

n个线程获取锁的时候,按照编号从小到大进行获取锁

这样就可以避免循环等待

浅谈死锁_死锁_05

这样哲学家5,想要4号筷子就得先释放5号筷子(锁) 避免死锁