• [Java] Lock(锁)的tryLock失败是否需要unlock?


    🥼/背景

    因为锁是非常重要且占用资源的,所以基本上都知道需要解锁
    常见的代码示例如下

            Lock lock = new ReentrantLock();
            try {
                lock.lock();
                //加锁后的操作xxx
    
            } catch (Exception e) {
                //异常处理
    
            } finally {
                //finally里面解锁,防止异常导致未释放锁
                lock.unlock();
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    没啥问题,但是如果用tryLock获取锁是否也是这样呢?(因为tryLock是非阻塞的,可能未拿到锁)如果没有获取到锁需要解锁么?如果未加锁解锁是不影响还是异常呢?正确的写法是怎样呢?

    🎡/试验

    其实实验的方法很简单,写个测试类实验一下就可以了
    1、如果未加锁就解锁是不影响还是异常呢

        @Test
        public void lockTest() {
            Lock lock = new ReentrantLock();
            lock.unlock();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    结果:
    在这里插入图片描述
    会产生异常报错

    2、tryLock没有获取到锁需要解锁么?

    因为需要让它获取锁失败,所以我们利用多线程模拟下并发情况下,获取失败的效果。

    代码如下,看起来没啥问题 (实际上有问题 -,-)

        @Test
        public void lockTest() {
            Lock lock = new ReentrantLock();
            for (int i = 0; i < 10; i++) {
                int finalI = i;
                new Thread(() -> {
                    try {
                        if (lock.tryLock()) {
                            System.out.println("hello" + finalI);
                        }
                    } finally {
                        lock.unlock();
                    }
                }).start();
            }
            boolean sleep = ThreadUtil.sleep(3000);
            System.out.println("over" + sleep);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    结果
    在这里插入图片描述 部分成功,未获取到锁的去解锁,会产生异常报错
    那么到这里其实我们就明白了,如果未获取到锁,我们是不需要去解锁的,去解锁反而还会产生异常。

    🎪/理解归纳

    正确优雅的写法(-,-):

        @Test
        public void lockTest() {
            Lock lock = new ReentrantLock();
            try {
                if (!lock.tryLock()) {
                    return;
                }
                //业务处理
                System.out.println("hello word!");
    
            } finally {
                lock.unlock();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    如果不是void 的方法就需要返回对象或者null

        public String getSomeData(String req) {
            Lock lock = new ReentrantLock();
            try {
                if (!lock.tryLock()) {
                    return null;
                }
                //业务处理
                System.out.println("hello word!" + req);
    
                return "some data result";
            } finally {
                lock.unlock();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    然而最麻烦的情况是异常情况下不想返回任何值,想抛出异常处理(比如触发事务回滚)

    而且我们使用Spring的全局异常拦截之后,我们可以直接把异常信息格式化成标准格式给前端,直接自
    定义异常比if else判断返回值方便且优雅的多(-,-)

    因为要判断是否加锁成功,那么代码写出来可能就是这样了。

        @Transactional(rollbackFor = Exception.class)
        public String getSomeData(String req) {
            Lock lock = new ReentrantLock();
            boolean locked = false;
            try {
                locked = lock.tryLock(2, TimeUnit.SECONDS);
                if (!locked) {
                    throw new BizException(Const.ResultCode.FAIL, "请稍后重试");
                }
    
                //业务处理 一般有事务操作
                System.out.println("hello word!" + req);
                return "some data result";
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                throw new BizException(Const.ResultCode.FAIL, "系统开小差了.");
            } finally {
                if (locked) {
                    lock.unlock();
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    不知道你们看着烦不烦,反正我是看着烦

                if (locked) {
                    lock.unlock();
                }
    
    • 1
    • 2
    • 3

    看着我难受(强迫症 Q,Q)

    🎏/解决方案

    其实解决方案也非常简单,就是把加锁的东西提出来就好了,不要和业务混合在一起

      	@Transactional(rollbackFor = Exception.class)
        public String getSomeData(String req) {
    
            Lock lock = new ReentrantLock();
            if (!lock.tryLock()) {
                throw new BizException(Const.ResultCode.FAIL, "请稍后重试");
            }
    
            try {
                //业务处理 一般有事务操作
                System.out.println("hello word!" + req);
    
                return "some data result";
            } finally {
                lock.unlock();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    先进行了锁的检查,那么就可以确保,后面的lock是存在的,可以放心的unlock.

    🧵/结论

    1、Lock 的unlock()方法必须要加锁成功才能执行,不然会抛异常
    2、不是把unlock()方法写在finally里面就一定正确,tryLock()如果加锁失败,在finally里面会再抛异常
    3、把加锁操作和业务分开,能避免大量判断,增加代码优雅可读性

    在这里插入图片描述
    (偷图,侵删)

  • 相关阅读:
    软件需求的三大层次,逐层细化的注意事项
    多线程之间获取不到值的问题
    数据结构02:线性表 链表习题02[C++]
    聊聊神经网络的优化算法
    我,PolarDB云原生数据库,5年来实现这些重磅技术创新
    JAVA知识——JAVA基础
    电商平台API接口采集电商平台淘宝天猫京东拼多多数据获取产品详情信息,销量,价格,sku案例
    【linux】SourceForge 开源软件开发平台和仓库
    任务需求分析中的流程图、用例图、er图、类图、时序图线段、图形的作用意义
    山西电力市场日前价格预测【2023-11-07】
  • 原文地址:https://blog.csdn.net/weixin_41979605/article/details/125483400