likes
comments
collection
share

闲谈一下 SemaphoreSemaphore 在项目中使用很少,直到我读了一个开源项目,原来 Semaphore 可以

作者站长头像
站长
· 阅读数 53

Semaphore 在项目中使用很少,直到我读了一个开源项目,原来 Semaphore 可以被恰当地使用的。

一、Semaphore 简介

Semaphore(信号量)是一种计数器,用于限制同时访问特定资源或执行某段代码的线程数量。

它内部维护了一个许可的数量,每当一个线程进入需要被保护的代码块时,就尝试获取一个许可或者多个;当离开这个代码块时,则释放该许可。

如果所有许可都被占用,那么新请求许可的线程将等待。

Semaphore信号量与 CountDownLatch 不同的是,它内部的计数器是递增的,并且在一开始初始化允许的最大值。

二、初识 Semaphore

第一次正式认识 Semaphore 还是在一款开源软件上。

项目地址:gitee.com/uzongn/java…(fork到自己的仓库了)

项目简单介绍: Aliyun LOG Java Producer 是一个易于使用且高度可配置的 Java 类库,专门为运行在大数据、高并发场景下的 Java 应用量身打造。

2.1 Semaphore 的用途

用于控制正在发送空间的大小,类似于限流。 在这个开源软件它是这么使用的。下面是参数说明:maxBlockMs 表示最大的可用空间

闲谈一下 SemaphoreSemaphore 在项目中使用很少,直到我读了一个开源项目,原来 Semaphore 可以 类的位置:com.aliyun.openservices.aliyun.log.producer.LogProducer

闲谈一下 SemaphoreSemaphore 在项目中使用很少,直到我读了一个开源项目,原来 Semaphore 可以

memoryController 用于控制当前机器中正在发送中的日志内存总大小。

com.aliyun.openservices.aliyun.log.producer.internals.LogAccumulator

闲谈一下 SemaphoreSemaphore 在项目中使用很少,直到我读了一个开源项目,原来 Semaphore 可以

在这段逻辑中, 由于发送的日志是并发的,为了防止发送占用内存过大, Semaphore 类型的变量 memoryController,用于控制正在发送的内存总大小。 这样避免过多的发送线程发送日志,导致内存溢出。

闲谈一下 SemaphoreSemaphore 在项目中使用很少,直到我读了一个开源项目,原来 Semaphore 可以

简而言之:限流,限制发送日志的内存占用量。

三、Semaphore 使用注意事项

Semaphore调用acquire()获取许可, try ... finallyfinally中释放许可。通常建议放在 finally 代码块中。

调用acquire()可能会进入等待,直到满足条件为止。也可以使用tryAcquire()指定等待时间:

另外:避免线程等待,可以设置最长等待时长。

获取许可不允许设置负数。

public boolean tryAcquire(int permits) {
    if (permits < 0) throw new IllegalArgumentException();
    return sync.nonfairTryAcquireShared(permits) >= 0;
}

四、应用场景

闲谈一下 SemaphoreSemaphore 在项目中使用很少,直到我读了一个开源项目,原来 Semaphore 可以

根据 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)方法实现许可的获取和释放,这种方式下,线程调度是公平的,即按照线程等待的顺序来分配许可。

闲谈一下 SemaphoreSemaphore 在项目中使用很少,直到我读了一个开源项目,原来 Semaphore 可以

默认是:非公平模式

 public Semaphore(int permits) {
        sync = new NonfairSync(permits);
 }

公平策略: 看当前线程节点的前驱节点是否也在等待获取该资源,如果是则放弃获取的权限,然后加入 AQS 阻塞队列,否则就直接获取。

六、总结

Semaphore 跟 CountDownLatch 一样,都复用 AQS 的能力。 另外都使用for(;;) 自旋 ,compareAndSetState 对 state 进行安全操作

整体上而言,Semaphore 在使用限流上经常被其他工具所替代,比如 guava#Ratelimiter 限流工具。所以在使用上不多。感兴趣可以再深入挖掘。

转载自:https://juejin.cn/post/7422636397658570786
评论
请登录