我靠!Semaphore里面居然有这么一个大坑! (3)

简介: 我靠!Semaphore里面居然有这么一个大坑! (3)

这两个地方的文档描述,有点玩文字游戏的意思了。稍不留神就被带进去了。


你仔细看:availablePermits 只是 return 当前可用的许可证数量。而 drainPermits 是 acquires and return,它先全部获取后再返回。


availablePermits 只是看看还有多少许可证,drainPermits 是拿走所有剩下的许可证。


所以在上面的场景下,这两个方法的返回值是一样的,但是内部处理完全内部不一样:



image.png


image.png


该方法就是释放指定数量许可证。释放,就意味着许可证的增加。就类似于刘能、谢广坤把他们各自的法拉利从停车位开出来,驶离停车场,这时停车场就会多两个停车位。


上面红框框起来的部分是它的主要逻辑。大家自己看一下,我就不翻译了,大概意思就是释放许可证之后,其他等着用许可证的线程就可以看一下释放之后的许可证数量是否够用,如果够就可以获取许可证,然后运行了。


该方法的精华在 599 到 602 行的说明中:


image.png


这句话非常关键:说的是执行 release 操作的线程不一定非得是执行了 acquire 方法的线程


开发人员,需要根据实际场景来保证 semaphore 的正确使用。


release 操作这里,大家都知道需要放到 finally 代码块里面去执行。但是正是这个认知,是最容易踩坑的地方,而且出了问题还非常不好排查的那种。


放肯定是要放在 finally 代码块里面的,只是怎么放,这里有点讲究。


我接合下一节的例子和 acquire 方法一起说明:


image.png


acquire 方法主要先关注我红框框起来的部分。


从该方法的源码可以看出,会抛出 InterruptException 异常。记住这点,我们在下一节,带入场景讨论。


release使用不当的大坑



我们还是带入之前停车的场景。假设赵四和我先把车停进去了,这个时候刘能、谢广坤他们来了,发现车位不够了,两个好基友嘛,就等着,非要停在一起


image.png


等了一会,我们一直没出来,门口看车的大爷出来对他们说:“我估摸着你们还得等很长时间,别等了,快走吧。”


于是,他们开车离去。


来,就这个场景,整一段代码:


public class ParkDemo {
    public static void main(String[] args) throws InterruptedException {
        Integer parkSpace = 3;
        System.out.println("这里有" + parkSpace + "个停车位,先到先得啊!");
        Semaphore semaphore = new Semaphore(parkSpace, true);
        Thread threadA = new Thread(new ParkCar(1, "布加迪", semaphore), "赵四");
        Thread threadB = new Thread(new ParkCar(2, "法拉利", semaphore), "刘能、谢广坤");
        Thread threadC = new Thread(new ParkCar(1, "劳斯莱斯", semaphore), "why哥");
        threadA.start();
        threadC.start();
        threadB.start();
        //模拟大爷劝退
        threadB.interrupt();
    }
}
class ParkCar implements Runnable {
    private int n;
    private String carName;
    private Semaphore semaphore;
    public ParkCar(int n, String carName, Semaphore semaphore) {
        this.n = n;
        this.carName = carName;
        this.semaphore = semaphore;
    }
    @Override
    public void run() {
        try {
            if (semaphore.availablePermits() < n) {
                System.out.println(Thread.currentThread().getName() + "来停车,但是停车位不够了,等着吧");
            }
            semaphore.acquire(n);
            System.out.println(Thread.currentThread().getName() + "把自己的" + carName + "停进来了," + "剩余停车位:" + semaphore.availablePermits() + "辆");
            //模拟停车时长
            int parkTime = ThreadLocalRandom.current().nextInt(1, 6);
            TimeUnit.SECONDS.sleep(parkTime);
            System.out.println(Thread.currentThread().getName() + "把自己的" + carName + "开走了,停了" + parkTime + "小时");
        } catch (InterruptedException e) {
            System.err.println(Thread.currentThread().getName() + "被门口大爷劝走了。");
        } finally {
            semaphore.release(n);
            System.out.println(Thread.currentThread().getName() + "走后,剩余停车位:" + semaphore.availablePermits() + "辆");
        }
    }
}



看着代码是没有毛病,但是运行起来你会发现,有可能出现这样的情况:



image.png


目录
相关文章
|
9天前
|
消息中间件 安全 算法
通透!从头到脚讲明白线程锁
线程锁在分布式应用中是重中之重,当谈论线程锁时,通常指的是在多线程编程中使用的同步机制,它可以确保在同一时刻只有一个线程能够访问共享资源,从而避免竞争条件和数据不一致性问题。
|
9月前
|
存储 Java
第二季:5公平锁/非公平锁/可重入锁/递归锁/自旋锁谈谈你的理解?请手写一个自旋锁【Java面试题】
第二季:5公平锁/非公平锁/可重入锁/递归锁/自旋锁谈谈你的理解?请手写一个自旋锁【Java面试题】
32 0
|
4月前
|
安全 Java
java多线程之Lock锁原理以及案例实现电影院卖票
java多线程之Lock锁原理以及案例实现电影院卖票
|
7月前
|
设计模式 安全 Java
JUC第十二讲:JUC锁 - 看不懂锁核心类 AQS 原理来打我
JUC第十二讲:JUC锁 - 看不懂锁核心类 AQS 原理来打我
|
12月前
|
存储 安全 Java
大白话讲解synchronized锁升级套路
synchronized锁是啥?锁其实就是一个对象,随便哪一个都可以,Java中所有的对象都是锁,换句话说,Java中所有对象都可以成为锁。 这次我们主要聊的是synchronized锁升级的套路
深入理解JUC:第六章:Semaphore信号灯
深入理解JUC:第六章:Semaphore信号灯
深入理解JUC:第六章:Semaphore信号灯
|
缓存 算法 Java
看完你就明白的锁系列之自旋锁
在上一篇文章 看完你就应该能明白的悲观锁和乐观锁 中我们已经学习到了什么是悲观锁和乐观锁、悲观锁和乐观锁的实现、优缺点分别是什么。其中乐观锁的实现之一 CAS 算法中提到了一个自旋锁的概念,为了全面理解 CAS 算法就首先需要了解一下自旋锁 是什么,自旋锁的适用场景和优缺点分别是什么,别着急,下面为你一一列举。
115 0
看完你就明白的锁系列之自旋锁
我靠!Semaphore里面居然有这么一个大坑! (2)
我靠!Semaphore里面居然有这么一个大坑! (2)
61 0
我靠!Semaphore里面居然有这么一个大坑! (2)
我靠!Semaphore里面居然有这么一个大坑! (1)
我靠!Semaphore里面居然有这么一个大坑! (1)
103 0
我靠!Semaphore里面居然有这么一个大坑! (1)
|
Java
我靠!Semaphore里面居然有这么一个大坑! (4)
我靠!Semaphore里面居然有这么一个大坑! (4)
83 0
我靠!Semaphore里面居然有这么一个大坑! (4)

热门文章

最新文章