Java - 死锁

·  阅读 39
什么是死锁?

多个线程因竞争同一个资源而造成互相等待的状态,在没有外力的作用下,线程将处于永远等待,这就是死锁。

死锁例子 - 嵌套同步代码
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代码,当发现类似的嵌套同步代码、获取锁顺序不一致,就得留意是否会出现死锁问题。

总结

当发现有嵌套同步代码时,可以通过保持请求锁顺序一致、不要嵌套同步代码,将同步代码的范围缩小、增加请求锁超时方法,解决死锁问题。 以上解决方案,不是完整的代码,只是提供了大致的方法,需要根据业务情况来修改代码。

以上分析有不对的地方,请指出,互相学习,谢谢哦!

分类:
Android
标签:
分类:
Android
标签:
收藏成功!
已添加到「」, 点击更改