什么是死锁?
多个线程因竞争同一个资源而造成互相等待的状态,在没有外力的作用下,线程将处于永远等待,这就是死锁。
死锁例子 - 嵌套同步代码
public class Test {
public Object mLock1 = new Object();
public Object mLock2 = new Object();
public void test1() {
synchronized (mLock1) {
//do something
synchronized (mLock2) {
//do something
}
}
}
public void test2() {
synchronized (mLock2) {
//do something
synchronized (mLock1) {
//do something
}
}
}
}
场景:线程A 执行test1方法,获取mLock1锁进入同步代码,线程B执行test2方法,获取mLock2锁进入同步代码,这时线程A竞争mLock2锁,发现锁被线程B持有,则线程A同步阻塞,与此同时,线程B竞争mLock1锁,发现锁被线程A持有,则线程B同步阻塞。线程A持有mLock1锁,等待mLock2,线程B持有mLock2,等待mLock1,在没有外力的作用下,线程A、线程B永远处于等待状态,死锁就发生了。
如何解决该类死锁问题
- 保持请求锁顺序一致;
代码如下,test1、test2都是先请求mLock1,后请求mLock2,这样就不会出现相互持有的情况。
public class Test {
public Object mLock1 = new Object();
public Object mLock2 = new Object();
public void test1() {
synchronized (mLock1) {
//do something
synchronized (mLock2) {
//do something
}
}
}
public void test2() {
synchronized (mLock1) {
//do something
synchronized (mLock2) {
//do something
}
}
}
}
- 不要嵌套同步代码,将同步代码的范围缩小;
代码如下,将test1拆分成test1和test3,将test2拆分test2和test4,在外部通过非同步方法调用来达到目的。
public class Test {
public Object mLock1 = new Object();
public Object mLock2 = new Object();
public void test1() {
synchronized (mLock1) {
//do something1
}
}
public void test3() {
synchronized (mLock2) {
//do something3
}
}
public void test2() {
synchronized (mLock2) {
//do something2
}
}
public void test4() {
synchronized (mLock1) {
//do something4
}
}
}
- 增加请求锁超时;
利用ReentrantLock的tryLock(1000,TimeUnit.SECONDS)方法,判断一定时间内是否能获取锁成功,成功则执行任务,否则释放自己持有的锁。
代码如下,test1在请求lock2 锁的是时候,设置了超时,如果在时间范围内没有获取到,则执行别的任务,结束方法释放lock1,test2方法同理;
public class Test {
public ReentrantLock lock1 = new ReentrantLock();
public ReentrantLock lock2 = new ReentrantLock();
public void test1() {
lock1.lock();//lock1加锁
//do something
boolean isLock = false;
try {
isLock = lock2.tryLock(1000, TimeUnit.SECONDS);
if (isLock) {//执行获取了锁的任务
//do something
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (isLock) {
lock2.unlock();
}
}
lock1.unlock();//lock1释放锁
}
public void test2() {
lock2.lock();//lock2加锁
//do something
boolean isLock = false;
try {
isLock = lock1.tryLock(1000, TimeUnit.SECONDS);
if (isLock) {//执行获取了锁的任务
//do something
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (isLock) {
lock1.unlock();
}
}
lock2.unlock();//lock2加锁
}
}
如何发现该类死锁问题?
加强编码规范以及review代码,当发现类似的嵌套同步代码、获取锁顺序不一致,就得留意是否会出现死锁问题。
总结
当发现有嵌套同步代码时,可以通过保持请求锁顺序一致、不要嵌套同步代码,将同步代码的范围缩小、增加请求锁超时方法,解决死锁问题。
以上解决方案,不是完整的代码,只是提供了大致的方法,需要根据业务情况来修改代码。
以上分析有不对的地方,请指出,互相学习,谢谢哦!
网友评论