Semaphore, 是JDK1.5的java.util.concurrent并发包中提供的一个并发工具类
Semaphore字面意思即信号量 , 个人认为比较容易理解的说法应该是 许可证管理器
官方的解释为
Semaphore内部主要通过AQS(AbstractQueuedSynchronizer)实现线程的管理 .
线程在运行时首先获取许可 , 如果成功 , 许可数就减1 , 线程运行 ; 当线程运行结束就释放许可 , 许可数就加1 ; 如果许可数为0 , 则获取失败 , 线程位于AQS的等待队列中 , 它会被其它释放许可的线程唤醒
Semaphore(int permits) : 初始化指定数量许可证的Semaphore
Semaphore(int permits, boolean fair) : 初始化指定数量许可证的Semaphore , 并指定是否公平模式(即FIFO先进先出原则)
boolean isFail() : 当前Semaphore是否是公平模式
int availablePermits() : 当前Semaphore可用的许可证数量
boolean hasQueuedThreads() : 判断当前Semaphore中是否存在正在等待许可证的线程
int getQueueLength() : 获取当前Semaphore中正在等待许可证的线程数量
void acquire() throws InterruptedException : 尝试获取一个可用的许可证 , 如果无可用许可证 , 当前线程会阻塞 , 如果线程被中断则抛出InterruptedException , 并停止阻塞继续执行
void acquire(int permits) throws InterruptedException : 尝试获取指定数量的可用许可证 , 如果无可用或数量不足 , 当前线程会阻塞 ,
如果线程被中断则抛出InterruptedException , 并停止阻塞继续执行
boolean tryAcquire() : 尝试获取1次可用许可证 , 非阻塞的 , 仅在调用该方法时尝试一次 , 获取到就继续执行并返回true . 获取不到也会停止等待继续执行 , 并返回false . 此外有重载方法tryAcquire(int permits)
boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException : 在限定时间内尝试获取1个可用许可证 , 如果获取到则继续执行返回true , 如果超时则继续执行并返回false,如果被中断则抛出一次异常并继续执行,此外有重载方法tryAcquire(int permits, long timeout, TimeUnit unit)
void acquireUninterruptibly() : 不可中断的尝试获取1个可用许可证 , 此外有重载方法acquireUninterruptibly(int permits)
int drainPermits() : 获取剩余所有可用的许可证
void release() : 释放1个可用的许可证 , 重载方法acquire(int permits)
package com.xing.apiLimiting;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.RandomUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
/**
* @title 信号量Semaphore
* @author Xingbz
* @createDate 2018-12-7
*/
public class SemaphoreDemo {
public static void main(String[] args) throws Exception {
// demo1();//Semaphore的初始化 / 基本属性 / 获取释放操作
// demo2();//多进程之间的获取/释放/等待
// demo3();//实例场景
}
/**
* @title Semaphore的初始化 / 基本属性 / 获取释放操作
* @author Xingbz
*/
private static void demo1() throws Exception {
Semaphore semaphore1 = new Semaphore(5);//初始化指定数量许可证的Semaphore
Semaphore semaphore2 = new Semaphore(5, true);//初始化指定数量许可证的Semaphore , 并指定为公平模式(FIFO)
System.out.println("是否公平模式 : semaphore1 " + semaphore1.isFair() + "; semaphore2 " + semaphore2.isFair());//是否公平模式FIFO
semaphore2.acquire();//尝试获取一个可用的许可证
System.out.println("成功获取1个可用许可证 ; 剩余可用许可证数量 : " + semaphore2.availablePermits());
semaphore2.release();//释放1个许可证
System.out.println("成功释放1个许可证 ; 剩余可用许可证数量 : " + semaphore2.availablePermits());
semaphore2.acquire(2);//尝试获取N个可用的许可证
System.out.println("成功获取2个可用许可证 ; 剩余可用许可证数量 : " + semaphore2.availablePermits());
semaphore2.release(2);//释放N个许可证 // 此时如果释放3个(>2) , 则可用数量亦会增加为6个?
System.out.println("成功释放2个许可证 ; 剩余可用许可证数量 : " + semaphore2.availablePermits() + "\n");
}
/**
* @title 多进程之间的获取/释放/等待
* @author Xingbz
*/
private static void demo2() throws Exception {
Semaphore semaphore = new Semaphore(5, true);
new Thread(() -> {//启动一个新线程获取所有可用许可证
System.out.println(Thread.currentThread().getName() + "尝试获取剩余所有可用的许可证 . . .");
int permits = semaphore.drainPermits();//获取剩余所有可用的许可证
System.out.println(Thread.currentThread().getName() + "成功获取" + permits + "个可用许可证 ; 剩余可用许可证数量 : " + semaphore.availablePermits());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
semaphore.release(permits);
System.out.println(Thread.currentThread().getName() + "成功释放" + permits + "个许可证 ; 剩余可用许可证数量 : " + semaphore.availablePermits());
}).start();
Thread.sleep(100);
System.out.println("\n[1]当前剩余可用的许可证数量 : " + semaphore.availablePermits());
System.out.println("[1]否存在正在等待许可证的线程 : " + semaphore.hasQueuedThreads());
System.out.println("[1]正在等待许可证的线程数量 : " + semaphore.getQueueLength());
new Thread(() -> {//启动另外一个新线程尝试获取1个许可证
try {
System.out.println(Thread.currentThread().getName() + "尝试获取1个可用许可证 . . .");
semaphore.acquire();//由于前一个线程获取了所有可用 , 所以这里会阻塞 , 直到前一个线程释放1个为止
System.out.println(Thread.currentThread().getName() + "成功获取1个可用许可证 ; 剩余可用许可证数量 : " + semaphore.availablePermits());
} catch (InterruptedException e) {
e.printStackTrace();
}
semaphore.release();
System.out.println(Thread.currentThread().getName() + "成功释放1个许可证 ; 剩余可用许可证数量 : " + semaphore.availablePermits());
}).start();
Thread.sleep(100);
System.out.println("\n[2]当前剩余可用的许可证数量 : " + semaphore.availablePermits());
System.out.println("[2]否存在正在等待许可证的线程 : " + semaphore.hasQueuedThreads());
System.out.println("[2]正在等待许可证的线程数量 : " + semaphore.getQueueLength());
new Thread(() -> {//启动另外一个新线程尝试获取2个许可证
try {
System.out.println(Thread.currentThread().getName() + "尝试获取2个可用许可证 . . .");
semaphore.acquire(2);
System.out.println(Thread.currentThread().getName() + "成功获取2个可用许可证 ; 剩余可用许可证数量 : " + semaphore.availablePermits());
} catch (InterruptedException e) {
e.printStackTrace();
}
semaphore.release(2);
System.out.println(Thread.currentThread().getName() + "成功释放2个许可证 ; 剩余可用许可证数量 : " + semaphore.availablePermits());
}).start();
Thread.sleep(100);
System.out.println("\n[3]当前剩余可用的许可证数量 : " + semaphore.availablePermits());
System.out.println("[3]否存在正在等待许可证的线程 : " + semaphore.hasQueuedThreads());
System.out.println("[3]正在等待许可证的线程数量 : " + semaphore.getQueueLength());
Thread.sleep(6000);
System.out.println("\n[终]当前剩余可用的许可证数量 : " + semaphore.availablePermits());
System.out.println("[终]否存在正在等待许可证的线程 : " + semaphore.hasQueuedThreads());
System.out.println("[终]正在等待许可证的线程数量 : " + semaphore.getQueueLength());
}
private static final ExecutorService executorService = Executors.newCachedThreadPool();
/**
* @title 实例场景
* @description
* 场景说明 :
* 1 . 模拟出口车辆许可的场景
* 2 . 路段有2个车辆出口
* 初始化一个permits=2的Semaphore对象
* 3 . 某时段内共有20个车辆依次出行
* 该Semaphore对象遵循公平(FIFO)原则 , 定义20个请求线程
* 4 . 每个车辆出去的速度不同 , 耗费时间也不同
* 5 . 有的车辆会一直等待到出口放行
* 调用acquireUninterruptibly()方法 , 不会中断
* 6 . 有的车辆没有耐心 , 等待时间超过自己预期 , 则不再排队 , 折返回去
* 调用tryAcquire(timeout,TimeUnit)方法 , 且等待超时
* 7 . 有的车辆很有耐心 , 但突然接到电话 , 不再排队 , 去其他地点了
* 调用了acquire()方法 , 且被中断
* @author Xingbz
*/
private static void demo3() throws Exception {
Semaphore semaphore = new Semaphore(2, true);
List futureList = new ArrayList<>(5);//某车队的车辆 , 排队中途会被电话中断
for (int i = 1; i <= 20; i++) {
if (i <= 10) {//前10辆车耐心排队等候许可
executorService.execute(new Car("耐心车辆" + i, semaphore, 1));
} else if (i <= 15) {//这5个车辆没有耐心 , 只愿等待少许
executorService.execute(new Car("折返车辆" + i, semaphore, 2));
} else {//这5个车辆被中断
Future future = executorService.submit(new Thread(new Car("中断车辆" + i, semaphore, 3)));
futureList.add(future);
}
}
Thread.sleep(10000);//整体排队10S后 , 某车队被电话集体召回
System.out.println("车队众人接到电话 , 集体终止排队");
futureList.forEach(f -> f.cancel(true));
}
@AllArgsConstructor
static class Car implements Runnable {
/** 车辆编号 */
private String carNo;
/** 许可证 */
private Semaphore semaphore;
/** 行动方式 : 1一直等待直到许可 ; 2没有耐心折返回去 ; 3等待中被中断 */
private int type;
/**
* @title 许可
* @author Xingbz
*/
@Override
public void run() {
switch (type) {
case 1: //该车辆很有耐心 ,一直等到许可
semaphore.acquireUninterruptibly();
try {
Thread.sleep(RandomUtils.nextLong(3000, 4000));//正在许可 . . .
} catch (InterruptedException e) {
}
semaphore.release();//许可完毕 , 将许可证归还
System.out.println(carNo + "已成功许可 . ");
break;
case 2://该车辆没有耐心 , 等待1s没有获取许可证 , 就折返了
try {
if (semaphore.tryAcquire(RandomUtils.nextInt(6000, 11000), TimeUnit.MILLISECONDS)) {
Thread.sleep(RandomUtils.nextLong(3000, 4000));//正在许可 . . .
semaphore.release();//许可完毕 , 将许可证归还
System.out.println(carNo + "已成功许可 . ");
} else {//超时则不再等待
System.out.println(carNo + "折返 . ");
}
} catch (InterruptedException e) {
}
break;
case 3://该车辆等待中被中断
try {
semaphore.acquire();
Thread.sleep(RandomUtils.nextLong(3000, 4000));//正在许可 . . .
semaphore.release();
System.out.println(carNo + "已成功许可 . ");
} catch (InterruptedException e) {//被电话 中断 , 不再等待
System.out.println(carNo + "中断 , 驶向他处 . ");
}
}
}
}
}