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();
@Override
public 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();
@Override
public 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();
}
@Override
public 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更高
- 多使用并发集合少使用同步集合