Semaphore信号量

66 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第22天,点击查看活动详情

1、Semaphore 是什么

Semaphore 是一种基于计数的信号量。它可以设定一个阈值,基于此,多个线程竞争获取许可信号,做完自己的申请后归还,超过阈值后,线程申请许可信号将会被阻塞。Semaphore可控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理的使用资源。用来构建一些对象池,资源池之类的,比如数据库连接池等。

2、使用场景

Semaphore的基本使用场景是限制一定数量的线程能够去执行,比如限流,资源有明确访问数量限制的场景。

比如:数据库连接池,同时进行连接的线程有数量限制,连接不能超过一定的数量,当连接达到了限制数量后,后面的线程只能排队等前面的线程释放了数据库连接才能获得数据库连接。

比如:停车场场景,车位数量有限,同时只能容纳多少台车,车位满了之后只有等里面的车离开停车场外面的车才可以进入。

3、Semaphore常用方法说明

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

4、semaphore实现停车场提示牌功能。

假设停车场容纳总停车量10;当一辆车进入停车场后,显示牌的剩余车位数相应的减1;每有一辆车驶出停车场后,显示牌的剩余车位数相应的加1;停车场剩余车位不足时,车辆只能在外面等待。

//停车场同时容纳的车辆10
private  static  Semaphore semaphore=new Semaphore(10);
public static void semaphore(){
    //模拟100辆车进入停车场
    for(int i=0;i<100;i++){
        Thread thread=new Thread(new Runnable() {
            public void run() {
                try {
                    System.out.println("===="+Thread.currentThread().getName()+"来到停车场");
                    if(semaphore.availablePermits()==0){
                        System.out.println("车位不足,请耐心等待");
                    }
                    semaphore.acquire();//获取令牌尝试进入停车场
                    System.out.println(Thread.currentThread().getName()+"成功进入停车场");
                    Thread.sleep(new Random().nextInt(10000));//模拟车辆在停车场停留的时间
                    System.out.println(Thread.currentThread().getName()+"驶出停车场");
                    semaphore.release();//释放令牌,腾出停车场车位
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },i+"号车");
        thread.start();
    }
}