Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何在一个时间段内限制线程数_Java_Multithreading_Concurrency_Critical Section - Fatal编程技术网

Java 如何在一个时间段内限制线程数

Java 如何在一个时间段内限制线程数,java,multithreading,concurrency,critical-section,Java,Multithreading,Concurrency,Critical Section,我使用的服务在1秒内发出5个请求后开始阻止请求 在Spring中使用Java,我正在寻找一种对线程进行排队的方法,这种方法可以使多达5个线程在一秒钟内访问关键部分,并且任何其他线程都可以排队,一旦有带宽可以继续,就可以释放它们 目前,我尝试使用锁执行此操作,但它会导致线程始终等待1/5秒,即使我们不会在不睡眠的情况下达到每秒最大调用数 Lock l = new ReentrantLock(); try { l.lock(); //critical se

我使用的服务在1秒内发出5个请求后开始阻止请求

在Spring中使用Java,我正在寻找一种对线程进行排队的方法,这种方法可以使多达5个线程在一秒钟内访问关键部分,并且任何其他线程都可以排队,一旦有带宽可以继续,就可以释放它们

目前,我尝试使用锁执行此操作,但它会导致线程始终等待1/5秒,即使我们不会在不睡眠的情况下达到每秒最大调用数

    Lock l = new ReentrantLock();
    try {
        l.lock();
      //critical section
   } finally {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        l.unlock();
    }
在这个实现中,我从不超过每秒5次,但在一切都准备好返回给用户之后,我也会导致响应延迟200毫秒


我需要一个只在需要延迟时延迟线程的解决方案。在这种情况下,一秒钟内的第6个以上呼叫应该延迟,但前5个呼叫不需要延迟。同样,调用6-11也可能同时进行。

这种速率限制是微服务体系结构中的一个常见问题,因为它是解决级联故障这一更广泛问题的一部分。有很多库可以处理这个问题,其中一个最广泛使用的现代库叫做,它提供了一个
RateLimiter
实现。您可能想要与此非常接近的东西:

创建限制器:

RateLimiterConfig config=RateLimiterConfig.custom()
.limitRefreshPeriod(持续时间为秒(1))
.limitForPeriod(5)
.timeoutDuration(Duration.ofSeconds(4))//或失败前要等待多长时间
.build();
//创建注册表
RateLimiterRegistry RateLimiterRegistry=RateLimiterRegistry.of(配置);
//使用注册表
RateLimiter RateLimiter=rateLimiterRegistry
.rateLimiter(“someServiceLimiter”,配置);
使用它:

//装饰您对BackendService.doSomething()的调用
CheckedRunnable restrictedCall=费率限制器
.decorateCheckedRunnable(速率限制器、后端服务::doSomething);
//或者,可以使用注释:
@费率限制器(name=“SomeServiceLimitor”)
公共无效剂量测定法(){
//后端呼叫
}
我认为使用API解决它是最好的方法

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class BulkheadSemaphore {

    private Queue<Long> enterQueue = new LinkedList<>();
    private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
    private Semaphore semaphore;

    public BulkheadSemaphore(final Long timeLimit, final int concurrentThreadsLimit) {
        this.semaphore = new Semaphore(concurrentThreadsLimit);

        executor.scheduleAtFixedRate(() -> {
            final Long now = now();

            while (!enterQueue.isEmpty() && now - enterQueue.peek() >= timeLimit) {
                enterQueue.poll();
                semaphore.release();
            }
        }, timeLimit, 200, TimeUnit.MILLISECONDS);
    }

    private Long now() {
        return System.currentTimeMillis();
    }

    public void acquire() {
        try {
            semaphore.acquire();
        } catch (InterruptedException e) {
            // todo: handle exception
        }
    }

    public void release() {
        semaphore.release();
    }
}
import java.util.LinkedList;
导入java.util.Queue;
导入java.util.concurrent.*;
导入java.util.concurrent.locks.Lock;
导入java.util.concurrent.locks.ReentrantLock;
公共级大头机{
private Queue enterQueue=new LinkedList();
private ScheduledExecutorService executor=Executors.newSingleThreadScheduledExecutor();
专用信号量;
公用大容量主机(最终长时间限制、最终整数并发线程限制){
this.semaphore=新信号量(concurrentThreadsLimit);
执行者。日程安排固定日期(()->{
最后的长现在=现在();
而(!enterQueue.isEmpty()&&now-enterQueue.peek()>=timeLimit){
enterQueue.poll();
semaphore.release();
}
},timeLimit,200,TimeUnit.ms);
}
二等兵Long now(){
返回系统.currentTimeMillis();
}
公开无效获取(){
试一试{
semaphore.acquire();
}捕捉(中断异常e){
//todo:处理异常
}
}
公开无效释放(){
semaphore.release();
}
}
api非常简单:

  • 每个进入临界区的线程,调用
    bulkheademaphore.acqure()
  • 外部调用执行完成后,调用
    bulkheademaphore.release()
  • 为什么它能解决这个问题

    • 此信号量为进入 很早以前的关键部分
    • 它以一定的速率释放其许可(我将其设置为200ms,但可以更小)。它还保证,如果一个工作单元快速完成,下一个线程将能够启动一个新的工作单元
    • 一些线程仍然会面临冗余等待,但这种情况并非每次都会发生,它们最多会花费200毫秒
    由于请求需要时间,我将
    timeLimit
    设置为1.5秒,以符合您的1秒限制


    另外,别忘了关闭executor服务

    试试Guava的RateLimitor。为什么要限制每秒,而不是一次只允许5次呢?@daniu当一次完成另一次时,需要让一个人进来。如果他们完成得很快,一秒钟内可能会执行5次以上。上面的关键代码部分使用第三方api,每秒调用5次后,该api将拒绝其他调用。一旦您决定要执行的操作,您需要释放锁。在执行实际工作时,无法保持锁定。我添加了依赖项,但对于@RateLimiter,我得到一个错误,它不是有效的注释。知道我遗漏了什么吗?注释RateLimitor和实现RateLimitor的名称相同。因此,如果在同一个文件中同时使用这两个文件,则需要指定其中一个文件的整个包。e、 g.
    @io.github.resilience4j.ratelimiter.annotation.ratelimiter
    谢谢。我能够编译它,但仍然能够让代码执行超过limitForPeriod。我在包含关键部分的同一个类中有这个逻辑。配置是否应该在其他地方?我在文档中看到他们将其放在yaml中,但这不是为我编译的,你使用的是
    io.github.resilience4j:resilience4j-spring-boot2
    依赖关系吗?如果仍然有问题,您可能希望使用不带注释的方法。