Semaphore 也叫信号量、信号灯,是计算机并发编程的一种思想
信号量模型是由计算机科学家 Dijkstra 在 1965 年提出,一直是并发控制的利器
直到 1980 年管程模型被提出,越来越多的语言使用管程来实现并发控制
信号量和管程是可以相互实现的,管程比信号量更容易使用
信号量模型
信号量模型由一个计数器、一个等待队列、三个原子操作 init、down、up 组成
init 设置计数器的初值
down 操作使计数器减一,若计数器 < 0,则当前线程被阻塞
up 操作使计数器加一,若计数器 <= 0,则唤醒等待队列的一个线程(> 0 意味着没有线程阻塞)
JDK 中的信号量实现是并发包下的 Semaphore 类
Semaphore 类构造方法中需要传入初值 int,可选第二参数是 boolean,表示是否使用公平锁
Semaphore 类的 acquire 和 release 方法分被对应 down 和 up 操作
Semaphore 其他 API
availablePermits:返回计数器当前值
drainPermits:将计数器置为 0,并返回减少的值
reducePermits:将计数器减少指定值
利用 Semaphore 保证互斥访问
class Adder {
static int count;
//初始化信号量
static final Semaphore s = new Semaphore(1);
//用信号量保证互斥
static void addOne() {
try {
s.acquire();
count += 1;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
s.release();
}
}
}
多个线程执行 addOne,一个线程执行 acquire 后计数器为 0,可以继续执行,其他线程 acquire 后计数器小于 0,无法继续执行,只能等待有线程 release 时唤醒一个等待队列中的线程
利用 Semaphore 实现共享锁
实现一个简易限流器
class ObjectPool<T, R> {
private final List<T> pool = new Vector<>();
private final Semaphore sem;
// 构造函数
ObjectPool(int size, List<T> list) {
// 添加资源
pool.addAll(list);
// 设置可同时访问的线程数量
sem = new Semaphore(size);
}
// 消费池中的对象
R consume(Function<T, R> func) {
T t = null;
try {
sem.acquire();
// 使用 Vector 保证原子
t = pool.remove(0);
return func.apply(t);
} catch (Exception e) {
return null;
} finally {
// 消费完成后放回
pool.add(t);
sem.release();
}
}
}
多个线程同时从 ObjectPool 中取对象,只有指定数量的线程可以取到,其他线程将等待,ObjectPool 中的对象是重复利用的