我靠!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


目录
相关文章
|
3月前
|
安全 Java 开发者
Java多线程同步:synchronized与Lock的“爱恨情仇”!
Java多线程同步:synchronized与Lock的“爱恨情仇”!
87 5
|
3月前
|
Java 开发者
解锁Java并发编程的秘密武器!揭秘AQS,让你的代码从此告别‘锁’事烦恼,多线程同步不再是梦!
【8月更文挑战第25天】AbstractQueuedSynchronizer(AQS)是Java并发包中的核心组件,作为多种同步工具类(如ReentrantLock和CountDownLatch等)的基础。AQS通过维护一个表示同步状态的`state`变量和一个FIFO线程等待队列,提供了一种高效灵活的同步机制。它支持独占式和共享式两种资源访问模式。内部使用CLH锁队列管理等待线程,当线程尝试获取已持有的锁时,会被放入队列并阻塞,直至锁被释放。AQS的巧妙设计极大地丰富了Java并发编程的能力。
45 0
|
4月前
|
安全 数据库连接 数据库
(六)手撕并发编程之基于Semaphore与CountDownLatch分析AQS共享模式实现
在上篇文章《深入剖析并发之AQS独占锁&重入锁(ReetrantLock)及Condition实现原理》中我们曾基于ReetrantLock锁分析了AQS独占模式的实现原理,本章则准备从Semaphore信号量的角度出发一探AQS共享模式的具体实现。共享模式与独占模式区别在于:共享模式下允许多条线程同时获取锁资源,而在之前分析的独占模式中,在同一时刻只允许一条线程持有锁资源。
|
3月前
|
算法 Java 调度
【多线程面试题二十】、 如何实现互斥锁(mutex)?
这篇文章讨论了在Java中实现互斥锁(mutex)的两种方式:使用`synchronized`关键字进行块结构同步,以及使用`java.util.concurrent.locks.Lock`接口进行非块结构同步,后者提供了更灵活的同步机制和扩展性。
|
6月前
|
Java 数据库
Semaphore(信号量)源码解读与使用
Semaphore(信号量)源码解读与使用
|
设计模式 安全 Java
JUC第十二讲:JUC锁 - 看不懂锁核心类 AQS 原理来打我
JUC第十二讲:JUC锁 - 看不懂锁核心类 AQS 原理来打我
|
机器学习/深度学习
十九、信号量和管程
十九、信号量和管程
十九、信号量和管程
我靠!Semaphore里面居然有这么一个大坑! (2)
我靠!Semaphore里面居然有这么一个大坑! (2)
85 0
我靠!Semaphore里面居然有这么一个大坑! (2)
|
Java
我靠!Semaphore里面居然有这么一个大坑! (4)
我靠!Semaphore里面居然有这么一个大坑! (4)
110 0
我靠!Semaphore里面居然有这么一个大坑! (4)
我靠!Semaphore里面居然有这么一个大坑! (1)
我靠!Semaphore里面居然有这么一个大坑! (1)
129 0
我靠!Semaphore里面居然有这么一个大坑! (1)