从有点基础开始学JUC:辅助类-Semaphore

107 阅读2分钟

辅助类-Semaphore

概述

顾名思义,就是信号灯,可以用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理的使用资源。

其主要有几个方法

方法描述
acquire()获取一个令牌,在获取到令牌、或者被其他线程调用中断之前线程一直处于阻塞状态。
acquire(int permits)从这个信号量获取给定数量的许可,阻塞直到所有许可都可用,或者线程被中断
acquireUninterruptibly()获取一个令牌,在获取到令牌之前线程一直处于阻塞状态(忽略中断)。
tryAcquire()尝试获得令牌,返回获取令牌成功或失败,不阻塞线程。
tryAcquire(long timeout, TimeUnit unit)尝试获得令牌,在超时时间内循环尝试获取,直到尝试获取成功或超时返回,不阻塞线程。
release()释放一个令牌,唤醒一个获取令牌不成功的阻塞线程。
hasQueuedThreads()等待队列里是否还存在等待线程。
getQueueLength()获取等待队列里阻塞的线程数。
drainPermits()清空令牌把可用令牌数置为0,返回清空令牌的数量。
availablePermits()返回可用的令牌数量。

什么意思?我们来看一个应用

例子

我们现在有一个餐厅,有客人来餐厅吃饭,但是餐厅的桌位有限,我们来看一下怎么实现

public class Restaurant {
​
    public static void main(String[] args) {
​
        //创建一个Semaphore对象,设置permits为3,代表餐厅有3个桌位
        Semaphore semaphore = new Semaphore(3);
​
        for (int i = 0; i < 6; ++i) {
​
            new Thread(() -> {
                try {
                    
                    //获取一个令牌,等于用户拿到位置
                    semaphore.acquire();
                    
                    System.out.println(Thread.currentThread().getName() + "拿到了位置");
                    
                    System.out.println("---" + Thread.currentThread().getName() + "用完餐了,离开了位置");
                    
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } finally {
                    
                    //释放一个令牌,等于用户用完餐后离开位置
                    semaphore.release();
                }
            }, "用户" + i).start();
​
        }
​
    }
​
}

看一下执行的结果

用户0拿到了位置
用户1拿到了位置
---用户1用完餐了,离开了位置
---用户0用完餐了,离开了位置
用户2拿到了位置
---用户2用完餐了,离开了位置
用户3拿到了位置
---用户3用完餐了,离开了位置
用户4拿到了位置
---用户4用完餐了,离开了位置
用户5拿到了位置
---用户5用完餐了,离开了位置
​
进程已结束,退出代码0

注意:

  1. 当调用new Semaphore(3) 方法时,默认会创建一个非公平的锁的同步阻塞队列,之后如果令牌数量不足,则会创建一个Node节点加入阻塞队列,挂起当前线程

  2. 释放令牌不会唤醒所有的节点,只是将队列中的第一个需要唤醒的线程进行唤醒

这就是这篇文章的内容了,欢迎大家的讨论,如有错漏,也请指出,谢谢~