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

this.n = n; this.carName = carName; this.semaphore = semaphore; } @Override publicvoidrun{ try{ if(semaphore.availablePermits < n) { System.out.println(Thread.currentThread.getName +"来停车,但是停车位不够了,等着吧"); } semaphore.acquire(n); System.out.println(Thread.currentThread.getName +"把自己的"+ carName +"停进来了,剩余停车位:"+ semaphore.availablePermits +"辆"); //模拟停车时长 intparkTime = ThreadLocalRandom.current.nextInt(1,6); TimeUnit.SECONDS.sleep(parkTime); System.out.println(Thread.currentThread.getName +"把自己的"+ carName +"开走了,停了"+ parkTime +"小时"); }catch(Exception e) { e.printStackTrace; }finally{ semaphore.release(n); System.out.println(Thread.currentThread.getName +"走后,剩余停车位:"+ semaphore.availablePermits +"辆"); } } } 运行后的结果如下(由于是多线程环境 , 运行结果可能不尽相同):

CSDN|Semaphore 里面居然有这么一个大坑!
本文插图
这次这个运行结果和我们预期的是一致的 。 并没有线程阻塞的现象 。那为什么之前的代码就会出现“在运行时 , 有时只会执行完线程A , 其线程B和线程C都静默了”这种现象呢? 是道德的沦丧 , 还是人性的扭曲?我带大家走进代码:
CSDN|Semaphore 里面居然有这么一个大坑!
本文插图
差异就体现在获取剩余通行证的方法上 。 上面是链接里面的代码 , 下面是我自己写的代码 。说实在的 , 链接里面的代码我最开始硬是眼神编译了一分钟 , 没有看出问题来 。当我真正把代码粘到 IDEA 里面 , 跑起来后发现当最先执行了 B 线程后 , A、C 线程都可以执行 。 当最先执行 A 线程的时候 , B、C 线程就不会执行 。我人都懵逼了 , 反复分析 , 发现这和我认知不一样啊!于是我陷入了沉思:
CSDN|Semaphore 里面居然有这么一个大坑!
本文插图
过了一会 , 保洁大爷过来收垃圾 , 问我:“hi , 小帅哥 , 你这瓶红牛喝完了吧?我把瓶子收走了啊 。 ”然后瞟了一眼屏幕 , 指着获取剩余许可证的那行代码对我说:“你这个地方方法调用错了哈 , 你再好好看看方法说明 。 ” System.out.println("剩余可用许可证: " + semaphore.drainPermits); 说完之后 , 拍了拍我的肩膀 , 转身离去 。 得到大师点化 , 我才恍然大悟 。
CSDN|Semaphore 里面居然有这么一个大坑!
本文插图
由于获取剩余可用许可证的方法是 drainPermits , 所以线程 A 调用完成之后 , 剩下的许可证为0 , 然后执行 release 之后 , 许可证变为 1 。 (后面会有对应的方法解释) 这时又是一个公平锁 , 所以 , 如果线程 B 先进去排队了 , 剩下的许可证不足以让 B 线程运行 , 它就一直等着 。 C 线程也就没有机会执行 。把获取剩余可用许可证的方法换为 availablePermits 方法后 , 正常输出:
CSDN|Semaphore 里面居然有这么一个大坑!
本文插图
这真的是一个很小的点 。 所谓当局者迷旁观者清 , 就是这个道理 。


推荐阅读