本文介绍死锁产生的条件及优化方案。
死锁产生
线程互相等待
常见的死锁有JDK死锁和数据库死锁。
以JDK死锁为例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40public class Deadlock {
static class Friend {
private final String name;
public Friend(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
//同步锁bow
public synchronized void bow(Friend bower) {
System.out.format("%s: %s"
+ " has bowed to me!%n",
this.name, bower.getName());
//调用同步锁bowBack
bower.bowBack(this);
}
//同步锁bowBack
public synchronized void bowBack(Friend bower) {
System.out.format("%s: %s"
+ " has bowed back to me!%n",
this.name, bower.getName());
}
}
public static void main(String[] args) {
final Friend alphonse =
new Friend("Alphonse");
final Friend gaston =
new Friend("Gaston");
new Thread(new Runnable() {
public void run() { alphonse.bow(gaston); }
}).start();
new Thread(new Runnable() {
public void run() { gaston.bow(alphonse); }
}).start();
}
}输出:
1
2Alphonse: Gaston has bowed to me!
Gaston: Alphonse has bowed to me!分析:
执行程序期望是alphonse向gaston鞠躬,并等待gaston还礼;gaston向alphonse鞠躬,并等待alphonse还礼。
两个线程,分别是alphonse线程和gaston线程;
alphonse线程传入gaston对象,gaston传入alphonse对象,均执行执行bowBack
。问题是:
如果单线程执行,例如alphonse线程执行,结果会是:1
2Alphonse: Gaston has bowed to me!
Gaston: Alphonse has bowed back to me!多线程执行时,alphonse和gaston互相鞠躬,但是均等待对方回礼,则在bowBack产生等待死锁。
alphonse和gaston均为对象锁,但是内部进行了互相调用
bowBack(Friend bower)
。alphonse获得对象锁,gaston获得对象锁。
alphonse同步调用bow,gaston同步调用bow;没有问题,不会死锁。
alphonse同步锁继续,使用gaston对象调用bowBack,但是gaston也在使用alphonse对象调用bowBack,产生问题。
alphonse->gaston->bowBack;gaston->alphonse->bowBack;因此alphonse和gaston对象锁都互相加锁且等待对方释放锁,导致死锁。
死锁查看
查看进程执行情况:
1
jstack -l 69733
或者采用VisualVM,查看两个对象锁处于等待状态
除了死锁还有活锁、饥饿锁
避免死锁方法
避免一个线程操作获取多个锁,例如上例中一个方法内获取两个对象锁
避免一个线程在锁内占用多个资源,尽量保证每个锁占用一个,例如上例一个方法期望锁定两个对象
采用定时锁,即lock.tryLock(timeout),即在限定时间内获取锁,获取不到则放弃,防止死锁等待
1
2
3
4
5
6
7
8
9/**
* @return {@code true} 如果当前线程请求到锁或者本来就拥有锁
* @throws InterruptedException if the current thread is interrupted
* @throws NullPointerException if the time unit is null
*/
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}对于数据库锁,加锁和解锁在一个数据库连接,否则解锁失败。同一个对象锁进行加锁和解锁操作。
1
2
3
4
5
6
7
8
9try {
//关闭事务自动提交(开启事务)
this.connection.setAutoCommit(false);
//无异常,手动提交
this.connection.commit();
} catch(Exception e) {
//当前;连接回滚
this.connection.rollback();
}
并发包JUC使用
参考Orace文档