[ReentrantLock] 破坏死锁四个必要条件
程序员文章站
2024-03-24 08:24:16
...
参考资料:https://www.bilibili.com/video/BV19E411k7Ni?p=78 北京大学《操作系统原理》
解决死锁的方法
1. 死锁预防:
2. 死锁避免
3. 死锁检测与解除
(-)死锁预防
死锁产生四个必要条件
- 互斥使用(资源独占) 一个资源每次只能给一个进程使用
- 占有且等待(请求和保持,部分分配) 进程在申请新的资源的同时保持对原有资源的占有
- 不可抢占(不可剥夺) 资源申请者不能强行的从资源占有者手中夺取资源, 资源只能由占有者自愿释放
- 循环等待 存在一个进程等待队列 {P1, P2, … , Pn}, 其中P1等待P2占有的资源,P2等待P3占有的资源,…Pn等待P1占有的资源,形成一个进程等待环路
破坏"互斥使用/资源独占条件"
- 资源转换技术:把独占资源变为共享资源
- SPOOLing技术的引入 解决不允许任何进程直接占有打印机的问题 设计一个“守护进程/线程”负责管理打印机,
进程需要打印时,将请求发给该daemon,由它完成 打印任务
破坏“占有且等待”条件
- 实现方案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锁");
}
}
}
}
}
- 实现方案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;
}
}
}
}
破坏“不可抢占”条件
- 实现方案:
当一个进程申请的资源被其他进程占用时,可以
通过操作系统抢占这一资源(两个进程优先级不同)
局限性:适用于状态易于保存和恢复的资源
CPU、内存
破坏“循环等待”条件
- 通过定义资源类型的线性顺序实现
- 实施方案:资源有序分配法 把系统中所有资源编号,进程在申请资源时必 须严格按资源编号的递增次序进行,否则操作系统 不予分配
循环等待场景:
模拟死锁现象:
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();
}
}
}
上一篇: 大白话聊聊mysql的悲观锁