1、死锁发生的条件
- 互斥
 - 环路
 - 请求并保持
 - 不可剥夺
 
2、死锁的影响
死锁在不同系统中是不一样的,这取决于系统对死锁的处理能力
数据库:检测并放弃事务
jvm中:无法自动处理
3、发生死锁的例子
两个线程相互持有并等待锁
/*** @Author: zhangjx* @Date: 2020/9/20 19:27* @Description: 必定发生死锁的例子*/public class MustDeadLock implements Runnable{int falg = 1;static Object o1 = new Object();static Object o2 = new Object();@Overridepublic void run() {if (falg == 1) {synchronized (o1){System.out.println("获取o1的锁");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (o2){System.out.println("获取o2的锁");}}}if (falg == 0) {synchronized (o2){System.out.println("获取o2的锁");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (o1){System.out.println("获取o1的锁");}}}}public static void main(String[] args) {MustDeadLock runnable1 = new MustDeadLock();runnable1.falg = 0;MustDeadLock runnable2 = new MustDeadLock();runnable2.falg = 1;new Thread(runnable1).start();new Thread(runnable2).start();}}
4、如何定位死锁
4.1 jstack(不是一定能检测出来)
1、查看java的pid
2、执行${JAVA_HOME}/bin/jstack pid
Found one Java-level deadlock:============================="Thread-1":waiting to lock monitor 0x000000002881fa38 (object 0x0000000716cdd788, a java.lang.Object),which is held by "Thread-0""Thread-0":waiting to lock monitor 0x00000000259f0c88 (object 0x0000000716cdd778, a java.lang.Object),which is held by "Thread-1"Java stack information for the threads listed above:==================================================="Thread-1":at com.imooc.thread_demo.deadlock.MustDeadLock.run(MustDeadLock.java:25)- waiting to lock <0x0000000716cdd788> (a java.lang.Object)- locked <0x0000000716cdd778> (a java.lang.Object)at java.lang.Thread.run(Thread.java:748)"Thread-0":at com.imooc.thread_demo.deadlock.MustDeadLock.run(MustDeadLock.java:39)- waiting to lock <0x0000000716cdd778> (a java.lang.Object)- locked <0x0000000716cdd788> (a java.lang.Object)at java.lang.Thread.run(Thread.java:748)Found 1 deadlock.
2、ThreadMXBean 代码
package com.imooc.thread_demo.deadlock;import java.lang.management.ManagementFactory;import java.lang.management.ThreadInfo;import java.lang.management.ThreadMXBean;import java.util.Arrays;/*** @Author: zhangjx* @Date: 2020/9/20 21:14* @Description:*/public class QryDeadLock implements Runnable{int falg = 1;static Object o1 = new Object();static Object o2 = new Object();@Overridepublic void run() {if (falg == 1) {synchronized (o1) {System.out.println("获取o1的锁");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (o2) {System.out.println("获取o2的锁");}}}if (falg == 0) {synchronized (o2) {System.out.println("获取o2的锁");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (o1) {System.out.println("获取o1的锁");}}}}public static void main(String[] args) throws InterruptedException {QryDeadLock runnable1 = new QryDeadLock();runnable1.falg = 0;QryDeadLock runnable2 = new QryDeadLock();runnable2.falg = 1;new Thread(runnable1).start();new Thread(runnable2).start();Thread.sleep(1000);ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();if(deadlockedThreads != null && deadlockedThreads.length > 0){Arrays.stream(deadlockedThreads).forEach(e -> {ThreadInfo threadInfo = threadMXBean.getThreadInfo(e);System.out.println("发现死锁 :" + threadInfo.getThreadName() + " " + threadInfo.getLockName());});}}}
5、如何修复死锁
1、避免策略
避免相反获取锁顺序的发生,用hash值决定两把锁获取的顺序
2、检测与恢复策略
一段时间检测是否有死锁,如果有就剥夺某个资源,打开死锁
3、鸵鸟策略(不处理)
6、实际工程中如何避免死锁
1、设置超时时间
Lock的tryLock(Long timeOut,TimeUnit unit)
syncronized不具备尝试锁的能力
获取锁失败:打日志,发邮件报警,重启等
package com.imooc.thread_demo.deadlock;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;/*** @Author: zhangjx* @Date: 2020/9/20 22:14* @Description: 设置超时时间解决死锁*/public class TryLockDeadLock implements Runnable{static Lock lock1 = new ReentrantLock();static Lock lock2 = new ReentrantLock();int flag = 0;public static void main(String[] args) {TryLockDeadLock runnable1 = new TryLockDeadLock();runnable1.flag = 0;TryLockDeadLock runnable2 = new TryLockDeadLock();runnable2.flag = 1;new Thread(runnable1).start();new Thread(runnable2).start();}@Overridepublic void run() {for (int i = 0; i < 10; i++) {if (flag == 1) {try {if(lock1.tryLock(1000, TimeUnit.MILLISECONDS)){System.out.println("线程1获取到第一把锁lock1");if(lock2.tryLock(1100, TimeUnit.MILLISECONDS)){System.out.println("线程1获取到两把锁,lock1,lock2");Thread.sleep(2000);System.out.println("线程1执行完毕,退出");lock2.unlock();lock1.unlock();break;}else {System.out.println("线程1获取第二把锁lock2失败,释放第一把锁lock1,重试");lock1.unlock();}}else {System.out.println("线程1获取第一把锁lock1失败,重试");}} catch (InterruptedException ex) {ex.printStackTrace();}}if (flag == 0) {try {if(lock2.tryLock(800, TimeUnit.MILLISECONDS)){System.out.println("线程2获取到第一把锁lock2");if(lock1.tryLock(900, TimeUnit.MILLISECONDS)){System.out.println("线程2获取到两把锁 lock2,lock1");Thread.sleep(1000);System.out.println("线程2执行完毕,退出");lock1.unlock();lock2.unlock();break;}else {System.out.println("线程2获取第二把锁lock1失败,释放第一把锁lock2,重试");lock2.unlock();}}else {System.out.println("线程2获取第一把锁释放第一把锁lock2失败,已重试");}} catch (InterruptedException ex) {ex.printStackTrace();}}}}}
2、多使用并发类,而不是自己去设计锁
- ConcurrentLinkedDeque ,ConcurrentHashMap等
 - 实际应用中java.util.concurrent.atomic简单方便且效率比使用lock更高
 - 多使用并发集合少使用同步集合
 
