本文转载自微信公众号“源码兴趣圈”,作者:龙泰。转载本文请联系源码兴趣圈公众号。前言面试官:小子,你真的在??代码中使用过JUC并发包下的可重入锁ReentrantLock吗?混蛋:对,ReentrantLock是JDK提供的可重入锁。提供对共享资源的独占访问,一次只能一个线程获取锁面试官:你觉得ReentrantLock#lock方法是写在try语句外面还是写在里面混蛋:我...面试官:我们不合适,我们先去下结论吧。lock.lock()最规范的写法是写在try语句之外。lock.lock()Oracle文档在介绍锁的使用时有一段代码。我们以ReentrantLock为例。代码如下图:ReentrantLocklock=newReentrantLock();lock.lock();try{//accesstheresourceprotectedbythislock}finally{lock.unlock();}Q:为什么要把lock.unlock()放在finally语句块中?A:为了保证当当前线程执行过程中出现异常时,仍然可以释放锁,避免死锁。我们把上面的代码改一下,看看会有什么影响。ReentrantLocklock=newReentrantLock();try{lock.lock();//accesstheresourceprotectedbythislock}finally{lock.unlock();}看起来还行,为什么一开始不推荐这篇文章呢?先说一下可能出现的问题,异常栈丢失,假设在lock.lock方法中有lock,如果出现异常(不用),那么就会进入finally语句块解锁,继续跟进。看看lock.lock()的源码中是如何处理lock.unlock()的。如果抛出异常,可能还没有获取到锁。那么在解锁源码中,当前线程和锁线程肯定是不相等的,所以会抛出IMSE(IllegalMonitorStateException)异常。我重写了ReentrantLock加锁代码的逻辑,里面抛出异常。让我们一起看看会发生什么。情况finalvoidlock(){//如果模拟锁失败,则抛出异常if(true){thrownewRuntimeException("已报错!!!");}if(compareAndSetState(0,1))setExclusiveOwnerThread(Thread.currentThread());否则获取(1);}根据下图可以看出异常栈在加锁的时候被“吞噬”了,无声无息地消失了。当然这只是一个例子,但是谁能保证加锁不成功就不会抛出异常呢?真正的BUG上面的代码例子中,lock写在try的第一行,出问题的可能性极低。这里给大家准备一份反面教材。不要有这种类似的行为。示例代码中,将锁放在try语句块中,然后在加锁之前有可能引发异常的代码。这很酷。谁用的爽结局,至于要不要把lock.lock()写在try语句块里,文章的结论是:lock.lock()加锁方法最好写在try块外。它是一种规范,而不是强制性的。如果一定要写在try中,请写在try语句块的第一行,否则在lock方法之前就没有可能引发异常的代码了。最后,如果你把lock放在你代码的try语句中,请参考第1点
