互请不循
死锁产生的原因及四个必要条件
产生死锁的原因主要是:
(1) 因为系统资源不足。
(2) 进程运行推进的顺序不合适。
(3) 资源分配不当等。
如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则
就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。
产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件 : 进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件 : 若干进程之间形成一种头尾相接的循环等待资源关系。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之
一不满足,就不会发生死锁。
死锁的解除与预防:
理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和
解除死锁。所以,在系统设计、进程调度等方面注意如何不让这四个必要条件成立,如何确
定资源的合理分配算法,避免进程永久占据系统资源。此外,也要防止进程在处于等待状态
的情况下占用资源。因此,对资源的分配要给予合理的规划。
====================================================================
假设有两个线程,分别代表两个饥饿的人,他们必须共享刀叉并轮流吃饭。他们都需要获得两个锁:共享刀和共享叉的锁。
假如线程 “A”获得了刀,而线程“B”获得了叉。线程“A”就会进入阻塞状态来等待获得叉,而线程“B”则阻塞来等待“A”所拥有的刀。这样就造成了死锁。
避免死锁的一个通用的经验法则是:
当几个线程都要访问共享资源A、B、C时,保证使每个线程都按照同样的顺序去访问它们,比如都先访问A,在访问B和C。
即让所有的线程按照同样的顺序获得一组锁。
死锁就是两个或两个以上的线程被无限的阻塞,线程之间相互等待所需资源。这种情况可能发生在当两个线程尝试获取其它资源的锁,而每个线程又陷入无限等待其它资源锁的释放,除非一个用户进程被终止。就 JavaAPI 而言,线程死锁可能发生在一下情况。
- 当两个线程相互调用 Thread.join ()
- 当两个线程使用嵌套的同步块,一个线程占用了另外一个线程必需的锁,互相等待时被阻塞就有可能出现死锁。
下面这道题,是考死锁的,比较简单,想两个问题:
1.什么时候会造成死锁
2.wait和notify释放了哪个锁,因为题目中有两个锁。
import java.util.LinkedList; public class DeadLockTest { LinkedList list = new LinkedList(); public synchronized void push(Object x) { System.out.println("push"); synchronized (list) { list.addLast(x); notify(); } } public synchronized Object pop() throws Exception { synchronized (list) { if (list.size() <= 0) { wait(); } return list.removeLast(); } } public static void main(String[] args) throws InterruptedException { final DeadLockTest deadLockTest = new DeadLockTest(); Thread thread = new Thread(new Runnable() { public void run() { try { deadLockTest.pop(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); thread.start(); Thread.sleep(2000); Thread threadPush = new Thread(new Runnable() { public void run() { try { deadLockTest.push("push thread"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); threadPush.start(); } }
当List为空时,pop的锁一直wait,需要唤醒,而push也获取不到List的锁,进而无法执行notify唤醒方法,所以死锁。
来源:http://tomyz0223.iteye.com/blog/1050230
Example2:
class DeadLockSample{ public final Object lock1 = new Object(); public final Object lock2 = new Object(); public void methodOne(){ synchronized(lock1){ ... synchronized(lock2){...} } } public void methodTwo(){ synchronized(lock2){ ... synchronized(lock1){...} } } }
假设场景:线程A调用methodOne(),获得lock1的隐式锁后,在获得lock2的隐式锁之前线程B进入运行,调用methodTwo(),抢先获得了lock2的隐式锁,此时线程A等着线程B交出lock2,线程B等着lock1进入方法块,死锁就这样被创造出来了。
Example3:
import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockOrdering { private Lock lock1 = new ReentrantLock(); private Lock lock2 = new ReentrantLock(); public void op1() { while(true) { if(lock1.tryLock()) {//先获取lock1 try { System.out.println("do sth in op1 with lock1..."); try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) {} System.out.println("after sleep in op1"); if(lock2.tryLock()) {//再获取lock2 try { System.out.println("do sth in op1 with lock2..."); return;//退出循环 } finally { lock2.unlock(); } } } finally { lock1.unlock(); } } } } public void op2() { //锁获取的顺序与op1相反 while(true) { if(lock2.tryLock()) {//先获取lock2 try { System.out.println("do sth in op2 with lock2..."); try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) {} System.out.println("after sleep in op2"); if(lock1.tryLock()) {//再获取lock1 try { System.out.println("do sth in op2 with lock1..."); return;//退出循环 } finally { lock1.unlock(); } } } finally { lock2.unlock(); } } } } public static void main(String... args) { final LockOrdering test = new LockOrdering(); new Thread() { public void run() { test.op1(); } }.start(); new Thread() { public void run() { test.op2(); } }.start(); } }
这只是个例子,简单的使用了tryLock,而实际上,带超时时间的重载版本的tryLock更实用。如果上述两个操作都同时做,op1获取lock1,op2获取lock2,op1将获取不到lock2,op2也获取不到lock1,然后op1释放lock1,op2释放lock2,接着又重复这样的操作,就可能导致死循环,当然,这只是理论上的一种可能性,实际CPU要做很多事情,并不会出现这类低概率事件。如果使用带超时时间的tryLock,且每次使用的时间都不同,则几乎可避免这类情况。这有点儿类似网络中的碰撞以及解决碰撞的二进制指数退避算法。
例子3来源:内置锁与LOCK
相关推荐
这六个 MySQL 死锁案例,能让你理解死锁的原因!.doc
这个项目收集了一些常见的 MySQL 死锁案例,大多数案例都来源于网络,并对其进行分类汇总,试图通过死锁日志分析出每种死锁的原因,还原出死锁现场。 实际上,我们在定位死锁问题时,不仅应该对死锁日志进行分析,...
docker mysql_一些常见的mysql死锁案例_笔记记录
NULL 博文链接:https://fs20041242.iteye.com/blog/1732749
描述数据库死锁的解决方法和预防措施,对于新人学习Oracle有很好的帮助
该程序是我写的博客“一起talk C栗子吧((第一百一十九回:C语言实例--线程死锁三)”的配套程序,共享给大家使用
某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题。 主要点: 过多的同步可能造成相互不释放资源。 从而互相等待,一般发生于同步中持有多个对象的锁。 解决:不要在同一个代码块中,同时...
写一个死锁的例子案例一:必然发生死锁案例二:两个账户转账案例三:多人多次转账发生死锁必须满足哪些条件如何定位死锁有哪些解决死锁问题的策略?线上发生死锁怎么办常见修复策略哲学家就餐问题问题描述代码演示...
java 一个死锁的例子 java 一个死锁的例子 java 一个死锁的例子
背景pop购药上线后解冻操作经常发生死锁,报错日志如下:死锁sql语句select id from order_pay_statusupdate order_p
前几篇文章介绍了用源码的方式来调试锁相关的信息,这里同样用这个工具来解决一个线上实际的死锁案例,也是我们介绍的第一个两条 SQL 就造成死锁的情况。因为线上的表结构比较复杂,做了一些简化以后如下 CREATE ...
0.背景在业务低峰通过pt-osc在线做DDL期间出现死锁,导致业务的SQL被回滚了,对应用不友好。本案例死锁发生的场景:pt-osc拷贝最后一个chunk-s
本案例涉及12个线程,其中4个线程之间产生了相互死锁,本文对死锁进行了具体分析。
主要介绍了Java模拟死锁发生之演绎哲学家进餐问题,结合具体演绎哲学家进餐问题的案例形式详细分析了死锁机制与原理,需要的朋友可以参考下
一个线上项目报的死锁,简要说明一下产生原因、处理方案和相关的一些点. 1、背景 这是一个类似数据分析的项目,数据完全通过LOAD DATA语句导入一个InnoDB表中。为方便描述,表结构简化为如下: Create table tb(id ...
7.3 死锁及调整案例 7.4 隔离级别与锁 7.5 最大化并发性 7.6 锁相关的性能问题总结 7.7 锁与应用程序开发 7.8 本章小结 第8章 索引设计与优化 8.1 索引概念 8.2 索引结构 8.3 理解索引访问机制 8.4 索引设计 8.5...