8. 如何避免死锁?
从上图我们就可以看出,产生死锁就是俩个或多个线程在申请资源时,自己需要的资源别别人持有、并阻塞。所以导致死锁。
如何解决:
减小锁的范围,尽量保证之锁定自己需要的资源,减小交叉持有资源情况
但是有些时候不得不持有多个资源,比如银行转账,我们必须同时获得两个账户上的锁,才能进行操作,两个锁的申请必须发生交叉。这时我们也可以打破死锁的那个闭环,在涉及到要同时申请两个锁的方法中,总是以相同的顺序来申请锁,比如总是先申请 id 大的账户上的锁 ,然后再申请 id 小的账户上的锁,这样就无法形成导致死锁的那个闭环。
我们知道导致死锁有一个因素是阻塞,所以如果我们不使用默认阻塞的锁,也是可以避免死锁的。我们可以使用 ReentrantLock.tryLock() 方法,在一个循环中,如果 tryLock() 返回失败,那么就释放以及获得的锁,并睡眠一小段时间。这样就打破了死锁的闭环。
package com.roundyuan.fanggateway.test; import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @Author: JavaPub * @License: https://github.com/Rodert/ * @Contact: https://javapub.blog.csdn.net/ * @Date: 2022/1/2 14:38 * @Version: 1.0 * @Description: ReentrantLock */ public class DeadLock { private static Lock lock1 = new ReentrantLock(); private static Lock lock2 = new ReentrantLock(); public static void deathLock() { new Thread() { @Override public void run() { while (true) { if (lock1.tryLock()) { try { //如果获取成功则执行业务逻辑,如果获取失败,则释放lock1的锁,自旋重新尝试获得锁 if (lock2.tryLock()) { try { System.out.println("Thread1:已成功获取 lock1 and lock2 ..."); break; } finally { lock2.unlock(); } } } finally { lock1.unlock(); } } System.out.println("Thread1:获取锁失败,重新获取---"); try { //防止发生活锁 TimeUnit.NANOSECONDS.sleep(new Random().nextInt(100)); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { @Override public void run() { while (true) { if (lock2.tryLock()) { try { //如果获取成功则执行业务逻辑,如果获取失败,则释放lock2的锁,自旋重新尝试获得锁 if (lock1.tryLock()) { try { System.out.println("Thread2:已成功获取 lock2 and lock1 ..."); break; } finally { lock1.unlock(); } } } finally { lock2.unlock(); } } System.out.println("Thread2:获取锁失败,重新获取---"); try { //防止发生活锁 TimeUnit.NANOSECONDS.sleep(new Random().nextInt(100)); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 5; i++) { deathLock(); } } }
说起死锁,银行家算法非常有必要了解:
银行家算法(Banker’s Algorithm)是一个避免死锁(Deadlock)的著名算法,是由艾兹格·迪杰斯特拉在1965年为T.H.E系统设计的一种避免死锁产生的算法。它以银行借贷系统的分配策略为基础,判断并保证系统的安全运行。 1、操作系统按照银行家指定的规则为进程分配资源,当进程首次申请资源时,需要测试该进程对资源的最大需求量,如果系统现存的资源可以满足它的最大需求量则按当前的申请资源分配资源,否则就推迟分配; 2、当进程在执行中继续申请资源时,先测试该进程本次申请的资源数,是否超过了该资源剩余的总量,若超过则拒绝分配资源,若能满足则按当前的申请量分配资源,否则也要推迟分配。
参考阅读:
如何快速排查死锁?如何避免死锁?