使用Semaphore 实现一个简单的限流器

本文介绍了Java中的Semaphore类如何用于实现简单的限流器。Semaphore通过控制并发访问的线程数量,实现了信号量模型,允许指定数量的线程并发执行。在示例代码中,创建了一个线程池和一个最大并发数为5的Semaphore,当任务提交超过限制时,额外的任务会被阻塞,待已有任务完成释放信号量后才能继续执行。这种方式对比锁,能支持更多的线程并发执行,适合用于限流场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用Semaphore 实现一个简单的限流器

java api

Java的api中,提供了semaphore这个线程同步的辅助类,用来控制同时访问共享资源的线程数量。

Semaphore提供的主要方法如下:

void acquire():获取一个信号量,在获得信号量前线程会一直阻塞。

void release():释放一个信号量。

int availablePermits(): 返回当前可用的信号量数。

boolean hasQueuedThreads(): 查询是否有等待获取信号量的线程。

实际上,semaphore实现的是并发编程领域的信号量模型。

信号量模型

信号量模型,是Dijkstra在1965年提出的理论,也是长时间并发编程领域的经典理论。现在并发编程领域主流用的比较多的是另外一个理论,就是管程,后面会单独介绍管程。

信号量模型,主要是由计数器,等待队列组成。

首先,要先初始化信号量模型,就是设置计数器的初始值。

然后,线程A来获取信号量,如果计数器中有信号量,那么线程得到信号量,同时将计数器减1

然后,线程A执行完,就释放之前得到的信号量,同时将计数器加1

如果在线程A获取得到信号量的执行过程中,有另外一个线程B来申请获取信号量,此时要看计数器是否还有信号量剩余,如果有,那么这另外的一个线程B就能够得到信号量执行,如果没有,这另外的线程B就要阻塞,进入等待队列中。

当线程A执行完,归还信号量,那么就会去唤醒等待队列中的信号量。

信号量计数器加减的过程,实际上也就是操作系统领域的PV操作,也有的叫PV原语

PV操作

操作系统中,可以把线程简单的抽象成三个状态,线程的执行过程,实际上就是这三态在某些条件下的转换

三态转换的过程就是PV操作来执行的,

P操作执行,获取到资源,信号量减1

V操作执行,释放资源,信号量加1

这里,就不深入的写操作系统相关的基础知识了。

信号量和锁


如果将信号量的计数器大小设置为1,那么这个信号量实际上跟java lock差不多,就是一个简单的锁的实现。 

但是,如果将信号量的计数器大小设置大于1,那么,这点是java lock 是做不到的,锁只能保障只有一个线程能够获得临界资源,但是信号量可以支持多个线程同时获得临界资源,这也就促成了信号量可以实现一个简单版本的限流器

代码实现

接下来,就是写一个简单的代码实现,首先建立一个线程池,线程池的大小是10,再新建一个semaphore变量,将大小设定为5。

大概的逻辑,首先提交任务到线程池,然后线程池执行任务,在执行任务的过程中,受semaphore信号量的控制,最多并发执行只有5个任务。

import java.util.concurrent.*;


public class SemaphoreLimitTest {

  // 定义一个执行线程池
  private final Executor executor = new ThreadPoolExecutor(10, 20, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(10));
  // 每次只能执行5个任务
  private final Semaphore semaphore = new Semaphore(5);

  private void process() {
    executor.execute(new Runnable() {
      @Override
      public void run() {
        try {
          semaphore.acquire();
          Thread.sleep(3000);
          semaphore.release();
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    });
  }


  // 模拟测试
  public static void main(String[] args) {
    final SemaphoreLimitTest semaphoreLimitTest = new SemaphoreLimitTest();
    // 同时进来8个任务
    for (int i = 0; i < 8; i++) {
      // 定义8个线程
      new Thread("线程" + i) {
        @Override
        public void run() {
          semaphoreLimitTest.process();
        }
      }.start();
    }
  }

}

semaphore的不足


信号量在释放后,要去唤醒等待队列中的阻塞线程,这个地方的实现是只能唤醒一个阻塞中的线程,而不能同时唤醒多个线程去争抢临界资源。

另外一个问题是,只能唤醒一个阻塞中的线程,但是线程被唤醒后,可能会出现临界条件又不满足了,那么这个线程又会进入阻塞

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值