「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。
信号量简介
-
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
-
利用 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 中的对象是重复利用的