闲谈一下 SemaphoreSemaphore 在项目中使用很少,直到我读了一个开源项目,原来 Semaphore 可以
Semaphore 在项目中使用很少,直到我读了一个开源项目,原来 Semaphore 可以被恰当地使用的。
一、Semaphore 简介
Semaphore
(信号量)是一种计数器,用于限制同时访问特定资源或执行某段代码的线程数量。
它内部维护了一个许可的数量,每当一个线程进入需要被保护的代码块时,就尝试获取一个许可或者多个;当离开这个代码块时,则释放该许可。
如果所有许可都被占用,那么新请求许可的线程将等待。
Semaphore信号量与 CountDownLatch 不同的是,它内部的计数器是递增的,并且在一开始初始化允许的最大值。
二、初识 Semaphore
第一次正式认识 Semaphore 还是在一款开源软件上。
项目地址:gitee.com/uzongn/java…(fork到自己的仓库了)
项目简单介绍: Aliyun LOG Java Producer 是一个易于使用且高度可配置的 Java 类库,专门为运行在大数据、高并发场景下的 Java 应用量身打造。
2.1 Semaphore 的用途
用于控制正在发送空间的大小,类似于限流。 在这个开源软件它是这么使用的。下面是参数说明:maxBlockMs 表示最大的可用空间
类的位置:com.aliyun.openservices.aliyun.log.producer.LogProducer
memoryController 用于控制当前机器中正在发送中的日志内存总大小。
com.aliyun.openservices.aliyun.log.producer.internals.LogAccumulator
在这段逻辑中, 由于发送的日志是并发的,为了防止发送占用内存过大, Semaphore 类型的变量 memoryController,用于控制正在发送的内存总大小。 这样避免过多的发送线程发送日志,导致内存溢出。
简而言之:限流,限制发送日志的内存占用量。
三、Semaphore 使用注意事项
Semaphore
调用acquire()
获取许可, try ... finally
在finally
中释放许可。通常建议放在 finally 代码块中。
调用acquire()
可能会进入等待,直到满足条件为止。也可以使用tryAcquire()
指定等待时间:
另外:避免线程等待,可以设置最长等待时长。
获取许可不允许设置负数。
public boolean tryAcquire(int permits) {
if (permits < 0) throw new IllegalArgumentException();
return sync.nonfairTryAcquireShared(permits) >= 0;
}
四、应用场景
根据 Semaphore 的特性,可以围绕 “限流” 场景,进行合理的落地。(注意是虚拟机级别的,非分布式)
五、Semaphore 内部结构
Semaphore
的核心在于其内部类Sync
,它继承自AbstractQueuedSynchronizer
(AQS),这是其核心。Sync
类通过getState()
和setState(int newState)
方法管理许可数量。
- 非公平模式:
NonfairSync
类通过nonfairTryAcquireShared(int acquires)
和nonfairTryReleaseShared(int releases)
方法实现许可的获取和释放,这种方式下,线程的调度可能不是公平的。 - 公平模式:
FairSync
类通过tryAcquireShared(int acquires)
和tryReleaseShared(int releases)
方法实现许可的获取和释放,这种方式下,线程调度是公平的,即按照线程等待的顺序来分配许可。
默认是:非公平模式
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
公平策略: 看当前线程节点的前驱节点是否也在等待获取该资源,如果是则放弃获取的权限,然后加入 AQS 阻塞队列,否则就直接获取。
六、总结
Semaphore 跟 CountDownLatch 一样,都复用 AQS 的能力。 另外都使用for(;;)
自旋 ,compareAndSetState
对 state 进行安全操作
整体上而言,Semaphore 在使用限流上经常被其他工具所替代,比如 guava#Ratelimiter 限流工具。所以在使用上不多。感兴趣可以再深入挖掘。
转载自:https://juejin.cn/post/7422636397658570786