一、什么是死锁
当两个或两个以上的线程在执行过程中,因为争夺资源而造成的一种相互等待的状态,由于存在一种环路的锁依赖关系而永远地等待下去,如果没有外部干涉,他们将永远等待下去,此时的这个状态称之为死锁 。
经典的 “哲学家进餐” 问题很好地描述了死锁状况:
5个哲学家去吃中餐,坐在一张圆桌旁,他们有5根筷子(而不是5双),并且每两个人中间放一根筷子,哲学家们要么在思考,要么在进餐,每个人都需要一双筷子才能吃到东西,并在吃完后将筷子放回原处继续思考,有些筷子管理算法 (1) 能够使每个人都能相对及时的吃到东西,但有些算法却可能导致一些或者所有哲学家都"饿死",后一种情况将产生死锁:每个人都拥有其他人需要的资源,同时有等待其他人已经拥有的资源,并且每个人在获取所有需要的资源之前都不会放弃已经拥有的资源 。筷子管理算法(1):一个饥饿的科学家会尝试获得两根临近的筷子,但如果其中一根正在被另一个科学家使用,那么他将放弃已经得到的那根筷子,并在等待几分钟之后尝试死锁:每个人都立即抓住自己左边的筷子,然后等待自己右边的筷子空出来,但同时又不放下已经拿到的筷子,形成一种相互等待的状态 。
饥饿:哲学家们都同时想吃饭,同时拿起左手边筷子,但是发现右边没有筷子,于是哲学家又同时放下左手边筷子,然后大家发现又有筷子了,又同时开始拿起左手边筷子,又同时放下,然后反复进行 。
文章插图
在线程A持有锁L并想获得锁M的同时,线程B持有锁M并尝试获得锁L,那么这两个线程将永远地等待下去,这种情况就是死锁形式(或者称为"抱死").
文章插图
二、死锁的四个必要条件
互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用 。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用完释放 。
请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放 。
不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放 。
环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{A,B,C,···,Z} 中的A正在等待一个B占用的资源;B正在等待C占用的资源,……,Z正在等待已被A占用的资源 。
三、死锁实例
/** * 死锁类示例 */public class DeadLock implements Runnable { public int flag = 1; //静态对象是类的所有对象共享的 private static Object o1 = new Object(), o2 = new Object(); @Overridepublic void run() {System.out.println("flag:{}"+flag);if (flag == 1) {//先锁o1,再对o2加锁,环路等待条件synchronized (o1) {try {Thread.sleep(500);}catch (Exception e) {e.printStackTrace();}synchronized (o2) {System.out.println("1");}}}if (flag == 0) {//先锁o2,在锁01synchronized (o2) {try {Thread.sleep(500);}catch (Exception e) {e.printStackTrace();}synchronized (o1) {System.out.println("0");}}} } public static void main(String[] args) {DeadLock td1 = new DeadLock();DeadLock td2 = new DeadLock();td1.flag = 1;td2.flag = 0;//td1,td2都处于可执行状态,但JVM线程调度先执行哪个线程是不确定的 。//td2的run()可能在td1的run()之前运行new Thread(td1).start();new Thread(td2).start(); }}1、当DeadLock 类的对象flag=1时(td1),先锁定o1,睡眠500毫秒
2、而td1在睡眠的时候另一个flag==0的对象(td2)线程启动,先锁定o2,睡眠500毫秒
3、td1睡眠结束后需要锁定o2才能继续执行,而此时o2已被td2锁定;
4、td2睡眠结束后需要锁定o1才能继续执行,而此时o1已被td1锁定;
5、td1、td2相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁 。
文章插图
动态锁顺序死锁:
// 资金转账到账号 public static void transferMoney(Account fromaccount, Account toAccount, DollarAmount amount) throws InsufficientFundsException { // 锁定汇款者的账户 synchronized (fromAccount) { // 锁定到账者的账户 synchronized (toAccount) { // 判断账户的余额不能为负数 if (fromAccount.getBalance().compareTo(amount) < 0) { throw new InsufficientFundsException(); } else { // 汇款者的账户减钱 fromAccount.debit(amount); // 到账者的账户增钱 toAccount.credit(amount); } } } }
推荐阅读
- 梦见很精致的碗 梦见很多漂亮的碗盘子是什么意思
- 茶之五色
- 为什么很多看起来不是很复杂的网站,都需要大量顶尖高手来开发?
- 人生 幸好有茶
- thinkphp5多语言怎么切换
- 开网店可以赚到钱吗 一般新手开网店能赚多少钱
- 天生发质不好怎么办
- 肝功能阳性是什么意思
- 茶与武侠
- 茶与红尘