因为锁是非常重要且占用资源的,所以基本上都知道需要解锁
常见的代码示例如下
Lock lock = new ReentrantLock();
try {
lock.lock();
//加锁后的操作xxx
} catch (Exception e) {
//异常处理
} finally {
//finally里面解锁,防止异常导致未释放锁
lock.unlock();
}
没啥问题,但是如果用tryLock获取锁是否也是这样呢?(因为tryLock是非阻塞的,可能未拿到锁)如果没有获取到锁需要解锁么?如果未加锁解锁是不影响还是异常呢?正确的写法是怎样呢?
其实实验的方法很简单,写个测试类实验一下就可以了
1、如果未加锁就解锁是不影响还是异常呢
@Test
public void lockTest() {
Lock lock = new ReentrantLock();
lock.unlock();
}
结果:

会产生异常报错
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);
}
结果
部分成功,未获取到锁的去解锁,会产生异常报错
那么到这里其实我们就明白了,如果未获取到锁,我们是不需要去解锁的,去解锁反而还会产生异常。
正确优雅的写法(-,-):
@Test
public void lockTest() {
Lock lock = new ReentrantLock();
try {
if (!lock.tryLock()) {
return;
}
//业务处理
System.out.println("hello word!");
} finally {
lock.unlock();
}
}
如果不是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();
}
}
然而最麻烦的情况是异常情况下不想返回任何值,想抛出异常处理(比如触发事务回滚)
而且我们使用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();
}
}
}
不知道你们看着烦不烦,反正我是看着烦
if (locked) {
lock.unlock();
}
看着我难受(强迫症 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();
}
}
先进行了锁的检查,那么就可以确保,后面的lock是存在的,可以放心的unlock.
1、Lock 的unlock()方法必须要加锁成功才能执行,不然会抛异常
2、不是把unlock()方法写在finally里面就一定正确,tryLock()如果加锁失败,在finally里面会再抛异常
3、把加锁操作和业务分开,能避免大量判断,增加代码优雅可读性

(偷图,侵删)