参考:https://zhuanlan.zhihu.com/p/98593407
Semaphore----信号灯
Semaphore翻译成字面意思为 信号量,Semaphore可以控同时访问的线程个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。
2个构造器:
-
public Semaphore(int permits) {
//参数permits表示许可数目,即同时可以允许多少线程进行访问
sync = new NonfairSync(permits); } -
public Semaphore(int permits, boolean fair) {
//这个多了一个参数fair表示是否是公平的,即等待时间越久的越先获取许可
sync = (fair)? new FairSync(permits) : new NonfairSync(permits); }
Semaphore类中比较重要的几个方法
,首先是acquire()、release()方法:
- public void acquire() throws InterruptedException { } //获取一个许可
- public void acquire(int permits) throws InterruptedException { }
//获取permits个许可 - public void release() { } //释放一个许可
- public void release(int permits) { } //释放permits个许可
acquire()用来获取一个许可,若无许可能够获得,则会一直等待,直到获得许可。
release()用来释放许可。注意,在释放许可之前,必须先获获得许可。
这4个方法都会被阻塞,如果想立即得到执行结果,可以使用下面几个方法:
- public boolean tryAcquire() { };
//尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false - public boolean tryAcquire(long timeout, TimeUnit unit) throws
InterruptedException { };
//尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false - public boolean tryAcquire(int permits) { };
//尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false - public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
throws InterruptedException { };
//尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
案例:
假若一个工厂有5台机器,但是有8个工人,一台机器同时只能被一个工人使用,只有使用完了,其他工人才能继续使用。那么我们就可以通过Semaphore来实现:
package com.example.dtest.threadTest.use;
import java.util.concurrent.Semaphore;
/*
* 实现一个容器,提供两个方法add、size,写两个线程:
线程1,添加10个元素到容器中
线程2,实时监控元素个数,当个数到5个时,线程2给出提示并结束
* */
public class SemaphoreTest {
public static void main(String[] args) {
int N = 8; //工人数
Semaphore semaphore = new Semaphore(5); //机器数目
for(int i=0;i<N;i++)
new Worker(i,semaphore).start();
}
static class Worker extends Thread{
private int num;
private Semaphore semaphore;
public Worker(int num,Semaphore semaphore){
this.num = num;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("工人"+this.num+"占用一个机器在生产...");
Thread.sleep(2000);
System.out.println("工人"+this.num+"释放出机器");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Semaphore常用方法说明
- acquire() 获取一个令牌,在获取到令牌、或者被其他线程调用中断之前线程一直处于阻塞状态。
-
acquire(int permits) 获取一个令牌,在获取到令牌、或者被其他线程调用中断、或超时之前线程一直处于阻塞状态。
-
acquireUninterruptibly() 获取一个令牌,在获取到令牌之前线程一直处于阻塞状态(忽略中断)。
-
tryAcquire() 尝试获得令牌,返回获取令牌成功或失败,不阻塞线程。
- tryAcquire(long timeout, TimeUnit unit)
尝试获得令牌,在超时时间内循环尝试获取,直到尝试获取成功或超时返回,不阻塞线程。
- release() 释放一个令牌,唤醒一个获取令牌不成功的阻塞线程。
- hasQueuedThreads() 等待队列里是否还存在等待线程。
- getQueueLength() 获取等待队列里阻塞的线程数。
- drainPermits() 清空令牌把可用令牌数置为0,返回清空令牌的数量。
- availablePermits() 返回可用的令牌数量。
用semaphore 实现停车场提示牌功能。
每个停车场入口都有一个提示牌,上面显示着停车场的剩余车位还有多少,当剩余车位为0时,不允许车辆进入停车场,直到停车场里面有车离开停车场,这时提示牌上会显示新的剩余车位数。
业务场景 :
1、停车场容纳总停车量10。
2、当一辆车进入停车场后,显示牌的剩余车位数响应的减1.
3、每有一辆车驶出停车场后,显示牌的剩余车位数响应的加1。
4、停车场剩余车位不足时,车辆只能在外面等待。
package com.example.dtest.threadTest.use;
import java.util.Random;
import java.util.concurrent.Semaphore;
public class SemaphoreTest02 {
// 停车场同时容纳的车辆10
private static Semaphore semaphore = new Semaphore(10);
public static void main(String[] args) {
// 模拟100辆车进入停车场
for(int i=0; i<100 ;i++){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("===="+Thread.currentThread().getName()+"来到停车场");
if(semaphore.availablePermits() == 0){
System.out.println("车位不足,耐心等待");
}
try {
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();
}
}
}