首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >避坑!-重入锁之ReentrantLock写法

避坑!-重入锁之ReentrantLock写法

作者头像
@派大星
发布2023-06-28 15:15:13
发布2023-06-28 15:15:13
4530
举报
文章被收录于专栏:码上遇见你码上遇见你

ReentrantLock

可重入锁

写法一(Oracle官方推荐):

代码语言:javascript
复制
ReentrantLock lock = new ReentrantLock();
public void calculate(){
    lock.lock();
    try{
        
    }finally{
        lock.unlock();
    }
}

写法二(大厂不推荐)

代码语言:javascript
复制
ReentrantLock lock = new ReentrantLock();
public void calculate(){
    try{
        lock.lock();
    }finally{
        lock.unlock();
    }
}

第二两种写法的缺点:

lock.lock()方法写在try里面

第二种写法会导致真实的崩溃信息被覆盖了,假设我们在lock.lock(); 上方加入这样一行代码int num = 1/0;代码如下:

代码语言:javascript
复制
ReentrantLock lock = new ReentrantLock();
public void calculate(){
    try{
        int num =  1/0;
        lock.lock();
    }finally{
        lock.unlock();
    }
}

此时正常我们所期望能出现的崩溃信息是java.langArithmeticException: /by zero,但是实际出现的异常则是:(出现的异常是:java.lang.IllegalMonitorStateException

这和我们所期望的异常是不一样的。这样在实际的应用中对我们排查问题是有很大的阻碍性的。真实的崩溃信息被覆盖掉了 原因是:

  • 因为上述代码无论是否会抛出异常,finally中的代码块都会被执行,最后会调用unlock()方法。而unlock()方法最终会调用ReentrantLock$Sync. tryRelease()方法,然后会判断当前线程是否是拥有锁的线程,如果不是则会抛出异常,从而会将真实的崩溃信息覆盖掉。
lock.lock()方法写在try里面的第一行

这时有人则会说。那我将代码int num = 1/0放在lock.lock()方法的下面不就好了;网上也有很多这样的说法(也就是将lock.lock()方法写在try代码块的第一行,保证lock()方法前面没有任何代码)。这样做对吗?难道不会出现问题嘛?答案是不一定。只能说出现问题的概率很低。

代码语言:javascript
复制
ReentrantLock lock = new ReentrantLock();
public void calculate(){
    try{
        lock.lock();
        int num =  1/0;
    }finally{
        lock.unlock();
    }
}

lock()方法的源码是这样说的:lock()方法可能会抛出异常,然后会执行finally代码块中的unlock()方法,在unlock()方法中,会检查当前线程是否是拥有锁的线程,如果不是则会抛出异常,同样会导致真实的崩溃信息丢失(导致覆盖掉lock.lock()出现异常的信息)。源码贴图:

这里有人又会问:那lock.lock()方法写在try外面和写在try中有什么区别嘛?

写在外面的话,lock()方法抛出异常的话就不会执行后续代码了。

既然unlcok()方法会抛出异常,为什么要写在finally代码块中呢?

因为如果程序出现异常,依然能够保证锁会被释放掉,避免死锁的发生。这里需要注意一点:unlock()方法需要放到finally代码块中的第一行,避免因为其它代码出现异常,导致unlock()方法无法执行。造成死锁。

lock.lock()方法写在try方法外面:

思考一下:写在外卖你一定不会有问题吗?答案:不一定。假设我们这样写:代码如下:

代码语言:javascript
复制
ReentrantLock lock = new ReentrantLock();
public void calculate(){
        lock.lock();
        int num =  1/0;
    try{
    }finally{
        lock.unlock();
    }
}

此时我们虽然将lock.lock()方法写在了try方法的外面,但是紧接着下一行代码出现了异常;并且此时已经加锁了同时没有进入try方法。这时会导致锁无法释放。导致死锁。导致其它线程无法获取到锁。

如何避免上面问题的出现

在使用可重入锁的时候,需要注意以下几点:

  • lock()方法必须写在try代码块外面
  • lock()方法和try代码块之间,不能有其它的代码,避免出现异常,导致锁无法释放,造成其它线程无法获取到锁
  • unlock()方法要放到finally代码块的第一行,避免因为其它代码块出现异常导致unlock()方法无法执行,锁无法释放。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-06-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码上遇见你 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ReentrantLock
    • 写法一(Oracle官方推荐):
    • 写法二(大厂不推荐)
    • 第二两种写法的缺点:
      • lock.lock()方法写在try里面
      • lock.lock()方法写在try里面的第一行
      • 既然unlcok()方法会抛出异常,为什么要写在finally代码块中呢?
      • lock.lock()方法写在try方法外面:
    • 如何避免上面问题的出现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档