CSDN|Semaphore 里面居然有这么一个大坑!( 五 )

threadA.start; threadC.start; threadB.start; //模拟大爷劝退 threadB.interrupt; } } privateintn; } @Override System.out.println(Thread.currentThread.getName +"把自己的"+ carName +"停进来了,"+"剩余停车位:"+ semaphore.availablePermits +"辆"); //模拟停车时长 }catch(InterruptedException e) { System.err.println(Thread.currentThread.getName +"被门口大爷劝走了 。 "); }finally{ } } } 看着代码是没有毛病 , 但是运行起来你会发现 , 有可能出现这样的情况:

CSDN|Semaphore 里面居然有这么一个大坑!
本文插图
why哥走后 , 剩余停车位变成了 5 辆?我是开着劳斯莱斯去给他们开发停车位去了吗?
在往前看日志发现 , 原来是刘能、谢广坤走后 , 显示了剩余停车位 3 辆 。问题就出在这个地方 。而这个地方对应的代码是这样的:
CSDN|Semaphore 里面居然有这么一个大坑!
本文插图
有没有一点恍然大悟的感觉 。50 行抛出了 InterruptedException , 导致明明没有获取到许可证的线程 , 执行了 release 方法 , 而该方法导致许可证增加 。在我们的例子里面就是刘能、谢广坤的车都还没停进去 , 走的时候门口的显示屏就增加了两个停车位 。这就是坑 , 就是你代码中的 BUG 潜伏地带 。而且还非常的危险 , 你想你代码里面莫名其妙的多了几个“许可证” 。 就意味着可能又多于你预期的线程在运行 。 很危险 。那么怎么修复呢? 答案已经呼之欲出了,这个地方需要 catch 起来 , 如果出现中断异常 , 直接返回:
CSDN|Semaphore 里面居然有这么一个大坑!
本文插图
跑起来 , 结果也正确 , 所有车都走了后 , 停车位还是只有 3 辆:
CSDN|Semaphore 里面居然有这么一个大坑!
本文插图
上面的写法还有一个疑问 , 如果我刚刚拿到许可证 , 就被中断了 , 怎么办? 看源码啊 , 源码里面有答案的 。
CSDN|Semaphore 里面居然有这么一个大坑!
本文插图
抛出 InterruptedException 后 , 分配给这个线程的所有许可证都会被分配给其他想要获取许可证的线程 , 就像通过调用 release 方法一样 。
增强release 你分析上面的问题会发现 , 导致问题的原因是没有获取到许可证的线程 , 调用了 release 方法 。我觉得这个设定 , 就是非常容易踩坑的地方 。 简直就是一个大坑! 我们可以就这个问题 , 对 release 方法进行增强 , 只有获取后的线程 , 才能调用 release 方法 。这一招我是在《Java高并发编程详解-深入理解并发核心库》里面学到的:
其中的 3.4.4 小节《扩展 Semaphore 增强 release》:
CSDN|Semaphore 里面居然有这么一个大坑!
本文插图
获取许可证的方法被修改成这样了(我只截取其中一个方法) , 获取成功后放入到队列里面:
CSDN|Semaphore 里面居然有这么一个大坑!
本文插图
里面的 release 方法修改成这样了 , 执行之前先看看当前线程是否是在队列里面:
CSDN|Semaphore 里面居然有这么一个大坑!


推荐阅读