欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[ReentrantLock] 破坏死锁四个必要条件

程序员文章站 2024-03-24 08:24:16
...

参考资料:https://www.bilibili.com/video/BV19E411k7Ni?p=78 北京大学《操作系统原理》

解决死锁的方法

1. 死锁预防:

2. 死锁避免

3. 死锁检测与解除


(-)死锁预防

死锁产生四个必要条件

  1. 互斥使用(资源独占) 一个资源每次只能给一个进程使用
  2. 占有且等待(请求和保持,部分分配) 进程在申请新的资源的同时保持对原有资源的占有
  3. 不可抢占(不可剥夺) 资源申请者不能强行的从资源占有者手中夺取资源, 资源只能由占有者自愿释放
  4. 循环等待 存在一个进程等待队列 {P1, P2, … , Pn}, 其中P1等待P2占有的资源,P2等待P3占有的资源,…Pn等待P1占有的资源,形成一个进程等待环路

破坏"互斥使用/资源独占条件"

  1. 资源转换技术:把独占资源变为共享资源
  2. SPOOLing技术的引入 解决不允许任何进程直接占有打印机的问题 设计一个“守护进程/线程”负责管理打印机,
    进程需要打印时,将请求发给该daemon,由它完成 打印任务

破坏“占有且等待”条件

  1. 实现方案1:要求每个进程在运行前必须一次性 申请它所要求的所有资源,且仅当该进程所要资 源均可满足时才给予一次性分配
    问题:资源利用率低;“饥饿”现象
    说明:线程模拟进程

模拟方案1:

package com.shen.face.lock;

/**
 * @author smx
 * @date 2020/4/8 21:05
 **/
public class DeadLock {

    public static void main(String[] args) throws InterruptedException {
        Share share = new DeadLock.Share(true);
        new Thread(share).start();
        Thread.sleep(100);
        share.setLockfun(false);
        new Thread(share).start();

    }
    
    private static class Share implements Runnable {
        private static Object lockA = new Object();
        private static Object lockB = new Object();
        private volatile boolean lockfun;
        public Share(boolean lockfun) {
            this.lockfun = lockfun;
        }

        public void setLockfun(boolean lockfun) {
            this.lockfun = lockfun;
        }

        @Override
        public void run() {

            try {
                if (lockfun) {
                    lockA();
                } else {
                    lockB();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
	
	 	//重点:方法上加 synchronized目的是保证每个线程一次性获取所有资源 ,不加synchronized会发生死锁现象
        public  synchronized void lockA() throws InterruptedException {
            synchronized (lockA) {
                Thread.sleep(3000);
                System.out.println(Thread.currentThread().getName() + ":进入lockA ...持有lockA锁");
                synchronized (lockB) {
                    System.out.println(Thread.currentThread().getName() + ":进入lockB ...持有lockB锁");
                }
            }
        }


	//重点:方法上加 synchronized目的是保证每个线程一次性获取所有资源,不加synchronized会发生死锁现象
        public  synchronized void lockB() throws InterruptedException {
            synchronized (lockB) {
                System.out.println(Thread.currentThread().getName()+":进入lockB ...持有lockB锁");
                synchronized (lockA) {
                    System.out.println(Thread.currentThread().getName()+":进入lockA ...持有lockA锁");
                }
            }
        }
    }

}
  1. 实现方案2:在允许进程动态申请资源前提下规 定,一个进程在申请新的资源不能立即得到满足 而变为等待状态之前,必须释放已占有的全部资
    源,若需要再重新申请

模拟方案2:

package com.shen.face.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author smx
 * @date 2020/4/8 21:05
 **/
public class PreventDeadLockReentranlock {

    public static void main(String[] args) throws InterruptedException {
        Share share = new PreventDeadLockReentranlock.Share(true);
        new Thread(share).start();

        Thread.sleep(100);
        share.setLockfun(false);
        new Thread(share).start();

    }


    private static class Share implements Runnable {

        ReentrantLock lockA = new ReentrantLock();
        ReentrantLock lockB = new ReentrantLock();


        private volatile boolean lockfun;

        public Share(boolean lockfun) {
            this.lockfun = lockfun;
        }

        public void setLockfun(boolean lockfun) {
            this.lockfun = lockfun;
        }


        @Override
        public void run() {

            if (lockfun) {
                lockA();
            } else {
                lockB();
            }

        }

        public  void lockA() {

            boolean flag = false;
            while (!flag)
            {
                lockA.lock();
                System.out.println(Thread.currentThread().getName() + ":进入lockA ...持有lockA锁");

                try {
                    lockB.tryLock(3,TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                	//释放线程所占资源lockA
                    lockA.unlock();
                    continue;

                }

                System.out.println(Thread.currentThread().getName() + ":进入lockB ...持有lockB锁");

                lockA.unlock();
                lockB.unlock();
                flag =true;
            }


        }


        public  void lockB() {

            boolean flag = false;
            while (!flag)
            {
                lockB.lock();
                System.out.println(Thread.currentThread().getName() + ":进入lockB ...持有lockB锁");
                try {
                    lockA.tryLock(3,TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                	//释放线程所占资源lockB
                    lockB.unlock();
                }
                System.out.println(Thread.currentThread().getName() + ":进入lockA ...持有lockA锁");
                lockB.unlock();
                lockA.unlock();
                flag =true;
            }




        }
    }

}

破坏“不可抢占”条件

  1. 实现方案:
    当一个进程申请的资源被其他进程占用时,可以
    通过操作系统抢占这一资源(两个进程优先级不同)
    局限性:适用于状态易于保存和恢复的资源
    CPU、内存

破坏“循环等待”条件

  1. 通过定义资源类型的线性顺序实现
  2. 实施方案:资源有序分配法 把系统中所有资源编号,进程在申请资源时必 须严格按资源编号的递增次序进行,否则操作系统 不予分配

循环等待场景:
[ReentrantLock] 破坏死锁四个必要条件
模拟死锁现象:

package com.shen.face.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author smx
 * @date 2020/4/8 21:05
 **/
public class PreventDeadLockReentranlockLoopWait {

    private static final ReentrantLock ROUTE_A = new ReentrantLock();
    private static final ReentrantLock ROUTE_B = new ReentrantLock();
    private static final ReentrantLock ROUTE_C = new ReentrantLock();
    private static final ReentrantLock ROUTE_D = new ReentrantLock();



    public static void routeC_D() {
        ROUTE_C.lock();
        System.out.println(Thread.currentThread().getName()+":"+"持有C准备获取D");
        mysleep(1);
        ROUTE_D.lock();
        System.out.println(Thread.currentThread().getName()+":"+"持有C获取到D");
        ROUTE_D.unlock();
        ROUTE_C.unlock();
    }

    public static void routeB_C() {


        ROUTE_B.lock();
        System.out.println(Thread.currentThread().getName()+":"+"持有B准备获取C");
        mysleep(1);
        ROUTE_C.lock();
        System.out.println(Thread.currentThread().getName()+":"+"持有B获取到C");
        ROUTE_C.unlock();
        ROUTE_B.unlock();

    }

    public static void routeD_A() {

        ROUTE_D.lock();
        System.out.println(Thread.currentThread().getName()+":"+"持有D准备获取A");
        mysleep(1);
        ROUTE_A.lock();
        System.out.println(Thread.currentThread().getName()+":"+"持有D获取到A");

        ROUTE_A.unlock();
        ROUTE_D.unlock();
    }

    public static void routeA_B() {

        ROUTE_A.lock();
        System.out.println(Thread.currentThread().getName()+":"+"持有A准备获取B");
        mysleep(1);
        ROUTE_B.lock();
        System.out.println(Thread.currentThread().getName()+":"+"持有A获取到B");

        ROUTE_B.unlock();
        ROUTE_A.unlock();
    }

    public static void main(String[] args) throws InterruptedException {

        new Thread(() -> routeB_C(), "car_2").start();
        new Thread(() -> routeC_D(), "car_1").start();
        new Thread(() -> routeA_B(), "car_4").start();
        new Thread(() -> routeD_A(), "car_3").start();

    }

    public static  void  mysleep(int num)
    {
        try {
            Thread.sleep(1000*num);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


}

模拟解决办法:

package com.shen.face.lock;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author smx
 * @date 2020/4/8 21:05
 **/
public class PreventDeadLockReentranlockLoopBreakWait {

    private static final ReentrantLock ROUTE_A = new ReentrantLock();
    private static final ReentrantLock ROUTE_B = new ReentrantLock();
    private static final ReentrantLock ROUTE_C = new ReentrantLock();
    private static final ReentrantLock ROUTE_D = new ReentrantLock();
    private static final Map<Integer, ReentrantLock> map = new ConcurrentHashMap<>();

    static {
        map.put(1, ROUTE_A);
        map.put(2, ROUTE_B);
        map.put(3, ROUTE_C);
        map.put(4, ROUTE_D);
    }


    public static void routeC_D() {

        while (true) {

            ReentrantLock rlock_c = map.remove(3);
            ReentrantLock rlock_d = map.remove(4);

            if (rlock_c == null || rlock_d == null) {
                if (rlock_c != null) {
                    map.put(3, rlock_c);
                }

                if (rlock_d != null) {
                    map.put(4, rlock_d);
                }
                continue;
            }


            rlock_c.lock();
            System.out.println(Thread.currentThread().getName() + ":" + "持有C准备获取D");
            mysleep(1);
            rlock_d.lock();
            System.out.println(Thread.currentThread().getName() + ":" + "持有C获取到D");
            rlock_c.unlock();
            rlock_d.unlock();

            map.put(3, rlock_c);
            map.put(4, rlock_d);
            break;
        }


    }

    public static void routeB_C() {

        while (true) {

            ReentrantLock rlock_b = map.remove(2);
            ReentrantLock rlock_c = map.remove(3);

            if (rlock_c == null || rlock_b == null) {
                if (rlock_c != null) {
                    map.put(3, rlock_c);
                }

                if (rlock_b != null) {
                    map.put(2, rlock_b);
                }
                continue;
            }


            rlock_b.lock();
            System.out.println(Thread.currentThread().getName() + ":" + "持有B准备获取C");
            mysleep(1);
            rlock_c.lock();
            System.out.println(Thread.currentThread().getName() + ":" + "持有B获取到C");
            rlock_c.unlock();
            rlock_b.unlock();

            map.put(2, rlock_b);
            map.put(3, rlock_c);
            break;
        }


    }

    public static void routeD_A() {

        while (true) {
            ReentrantLock rlock_d = map.remove(4);
            ReentrantLock rlock_a = map.remove(1);

            if (rlock_d == null || rlock_a == null) {
                if (rlock_d != null) {
                    map.put(4, rlock_d);
                }

                if (rlock_a != null) {
                    map.put(1, rlock_a);
                }
                continue;
            }

            rlock_d.lock();
            System.out.println(Thread.currentThread().getName() + ":" + "持有D准备获取A");
            mysleep(1);
            rlock_a.lock();
            System.out.println(Thread.currentThread().getName() + ":" + "持有D获取到A");

            rlock_d.unlock();
            rlock_a.unlock();

            map.put(4, rlock_d);
            map.put(1, rlock_a);
            break;
        }

    }

    public static void routeA_B() {

        while (true) {
            ReentrantLock rlock_a = map.remove(1);
            ReentrantLock rlock_b = map.remove(2);


            if (rlock_a == null || rlock_b == null) {
                if (rlock_a != null) {
                    map.put(1, rlock_a);
                }

                if (rlock_b != null) {
                    map.put(2, rlock_b);
                }
                continue;
            }

            rlock_a.lock();
            System.out.println(Thread.currentThread().getName() + ":" + "持有A准备获取B");
            mysleep(1);
            rlock_b.lock();
            System.out.println(Thread.currentThread().getName() + ":" + "持有A获取到B");

            rlock_b.unlock();
            rlock_a.unlock();

            map.put(2, rlock_b);
            map.put(1, rlock_a);
            break;
        }

    }

    public static void main(String[] args) throws InterruptedException {

        //线程栈空间隔离保证了线程安全
        new Thread(() -> routeB_C(), "car_2").start();
        new Thread(() -> routeC_D(), "car_1").start();
        new Thread(() -> routeA_B(), "car_4").start();
        new Thread(() -> routeD_A(), "car_3").start();

    }

    public static void mysleep(int num) {
        try {
            Thread.sleep(100 * num);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


}

相关标签: