Java多线程之死锁(死锁产生条件、手写简单死锁程序、破坏死锁)(面试常有)

时间:2024-10-14 07:56:15

也就是整个程序都满足上述的四个条件

<0>分析与解释。
  • "死锁类"不采用实现Runnable接口,重新run()方法。而是在测试中直接使用匿名的Runnable实现子类。()->{}。中括号里面就是实现的run()方法
  • "死锁类"创建的两个类的锁对象(static。在该"死锁"类的new两个即可。保证a与b"锁"是这个"死锁类"是共享的。


  • 测试类中到时候模拟两个线程,同时new两个"死锁"类的对象。d1、d2是不共享的,但是锁a、锁b是共享的资源。


  • 测试类中的new Thread(Runnable target)。参数内部是使用Lambda表达式(也可以使用匿名内部类)也就是在重新的run()方法里面进行调用方法操作(d1.执行死锁方法、d2.执行死锁方法)

  • 测试类中最后两个线程分别去使用start()方法。然后分别去拿各自的"锁a"、"锁b",然后阻塞(哈哈哈!)

  • "死锁类"中这里注意还需要有一个布尔类型的变量。去控制获取锁的顺序。顺序????就是为"true"时控制一个线程拿"锁"时,从a锁——>b锁。值为false时让另外一个线程拿"锁"时,从b锁——>a锁。flag不是设置成共享的(不是static)。

(具体详细代码如下。)

<1>简单死锁程序(类DeadLockDemo)
package com.fs;

/**
 * @Title: DeadLockDemo
 * @Author HeYouLong
 * @Package com.fs
 * @Date 2024/10/12 下午8:41
 * @description: 简单死锁程序演示
 */
public class DeadLockDemo {

    //锁a与锁b
    private static Object a = new Object();
    private static Object b = new Object();

    //控制拿锁的顺序,a——>b或者b——>a
    private boolean flag;
    //提供构造方法改布尔值

    public DeadLockDemo(boolean flag) {
        this.flag = flag;
    }

    public void testDeadLock() throws InterruptedException {
        if(flag){
            //a——>b
            synchronized (a){
                System.out.println(Thread.currentThread().getName()+"执行a锁代码块的资源");
                Thread.sleep(100); //模拟执行完后等待
                synchronized (b){
                    System.out.println(Thread.currentThread().getName()+"执行b锁代码块的资源");
                    Thread.sleep(100); //模拟执行完后等待
                }
            }
        }else {
            //b——>a
            synchronized (b){
                System.out.println(Thread.currentThread().getName()+"执行b锁代码块的资源");
                Thread.sleep(100); //模拟执行完后等待
                synchronized (a){
                    System.out.println(Thread.currentThread().getName()+"执行a锁代码块的资源");
                    Thread.sleep(100); //模拟执行完后等待
                }
            }
        }
    }
}
<2>测试类(Test)
package com.fs;
/**
 * @Title: Test
 * @Author HeYouLong
 * @Package com.fs
 * @Date 2024/10/13 上午10:59
 * @description: 测试类
 */
public class Test {
    public static void main(String[] args) {
        //创建不同对象
        DeadLockDemo d1 = new DeadLockDemo(true);
        DeadLockDemo d2 = new DeadLockDemo(false);

        Thread t1 = new Thread(()->{
            //线程t1的run()方法使用d1对象
            //但是在对象调用testDeadLock()使用的是共享资源的锁
            try {
                d1.testDeadLock();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },"线程1");

        Thread t2 = new Thread(()->{
            //线程t2的run()方法使用d2对象
            // 但是在对象调用testDeadLock()使用的是共享资源的锁
            try {
                d2.testDeadLock();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },"线程2");

        //启动线程
        t1.start();
        t2.start();
    }
}
<3>程序执行结果。(发生"死锁")

<4>破坏上面的"简单死锁程序"。
(I)破坏互斥条件。
(II)破坏不可抢占条件。
(III)破坏请求和保持条件。
  • 让两个线程"t1"、"t2"都执行完资源后释放完各自的"锁",然后再去申请其它"锁"。(不允许它保持
public void testDeadLock() throws InterruptedException {
        if(flag){
            //a——>b
            synchronized (a){
                System.out.println(Thread.currentThread().getName()+"执行a锁代码块的资源");
                Thread.sleep(100); //模拟执行完后等待
            }
            synchronized (b){
                System.out.println(Thread.currentThread().getName()+"执行b锁代码块的资源");
                Thread.sleep(100); //模拟执行完后等待
            }
        }else {
            //b——>a
            synchronized (b){
                System.out.println(Thread.currentThread().getName()+"执行b锁代码块的资源");
                Thread.sleep(100); //模拟执行完后等待
            }
            synchronized (a){
                System.out.println(Thread.currentThread().getName()+"执行a锁代码块的资源");
                Thread.sleep(100); //模拟执行完后等待
            }
        }
    }

(IIII)破坏循环等待条件。
  • 简单修改即可。不让他们请求锁时形成环路。(把顺序都变成"锁a"——>"锁b"