多线程死锁专题

1、死锁的概念

概念:死锁是指两个对象分别持有锁,并且相互等待对方释放锁的现象。

实例:A和B两个人吃意面,A拿勺子,B拿叉子,而要吃意面勺子和叉子都需要。A拿着勺子等着B放下叉子,B拿着叉子等着A放下勺子,这样两个人僵持下去,出现死锁。

/**
 * @author :16140
 * @description :   死锁的发生场景
 * @create :2021-08-07 20:18:00
 */
public class DeadLock {
    public static void main(String[] args) {
        Person person1 = new Person(0,"张三");
        Person person2 = new Person(1,"李四");
        person1.start();
        person2.start();
    }
}
​
/**
 * 勺子
 */
class Spoon{
​
}
​
/**
 * 叉子
 */
class Fork{
​
}
​
/**
 * 人
 */
class Person extends Thread{
    static Spoon spoon = new Spoon();
    static Fork fork = new Fork();
​
    int choice;
    String personName;
​
    public Person(int choice,String personName){
        this.choice = choice;
        this.personName = personName;
    }
​
    @Override
    public void run() {
        try {
            eat();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
​
    public void eat() throws InterruptedException {
        if (choice == 0){
            synchronized (spoon){
                System.out.println(this.personName + "拿到了勺子");
                Thread.sleep(1000);
                synchronized (fork){
                    System.out.println(this.personName + "拿到了叉子");
                }
            }
        }else{
            synchronized (fork){
                System.out.println(this.personName + "拿到了叉子");
                Thread.sleep(1000);
                synchronized (spoon){
                    System.out.println(this.personName + "拿到了勺子");
                }
            }
        }
    }
}

2、死锁的产生条件

  1. 某个资源在某时间内只允许一个线程操作;
  2. 该资源不能被夺走,只能由该线程自己释放;
  3. 某线程占用了一个资源,还想占用其他资源;
  4. 循环关系,即A需要B的资源,B需要C的资源,C需要A的资源。

如上面例子:

  1. 表示spoon和fork只能由一个线程占用;
  2. 表示两个人拿了一个资源还想要另一个资源;
  3. 表示这个资源你抢不走,只能等待释放;
  4. 表示这两个资源彼此形成循环,导致哪一方也不会提前释放。

3、解决办法

4个条件必须都满足才行。

解决方式:

  1. 可以多个线程同时使用这个资源,但是还是有多线程问题;
  2. 如果某个线程的资源不能夺走,而它又访问了其他资源而没拿到,那么就释放它,这样就允许其他线程访问。感觉这种方式非常礼貌,吃饭要排队,那我就先不排了,你们先吃。但是这种方式实现起来比较困难,所以很少使用;
  3. 既然你占了一个资源,还想占另一个,那么不如直接让一个拿到全部资源再执行。(《图解Java多线程设计模式》中的成对拿取方式)
  4. 既然是循环,那么不如按照一定分配次序来拿,这种方式不会造成资源的闭环。(《图解Java多线程设计模式》中的以相同顺序拿取资源)