限制在Java中运行某些代码段的线程数的最佳方法是什么?

use*_*351 6 java concurrency multithreading

我正在寻找一种方法来限制线程的数量,这些线程可以使用信号量或类似的方式在Java中运行某些代码段.

我们正在研究类似于Google Guava RateLimiter的东西- 但不是每秒限制数量调用,我们需要限制运行关键代码段的线程数.

需要这个的原因是我们使用的某些库在这里有问题所以我们只是寻找一个快速的解决方法.

And*_*gin 7

不过,Semaphore如果您小心的话,这是最好的选择(请参阅@Bex的答案),也可以使用ExecutorService。只需将您想保护的代码片段包装到一个Callable任务中,以防止无限的并发访问,然后将这些任务提交给executor服务:

// Task that will be executed
public class MyTask implements Callable<Void> {
    @Override
    public Void call() {
        // Do the work here
        return null;
    }
}

// Service to execute tasks in no more than 5 parallel threads
// Cache it after creation and use when you need to execute a task
int maxThreadsCount = 5;
ExecutorService executor = Executors.newFixedThreadPool(maxThreadsCount);

// Execute a task. It will wait if all 5 threads are busy right now.
executor.submit(new MyTask());
Run Code Online (Sandbox Code Playgroud)

使用,ExecutorService您还可以使用Runnable代替CallableinvokeAll()而不是execute,等待任务完成,取消任务,从任务中返回值并执行其他一些有用的操作。

Java 8使它更简单,您可以使用lambda代替定义任务类:

executor.submit(() -> {
    // Do the work here
});
Run Code Online (Sandbox Code Playgroud)

  • 需要记住的是,Executors 工厂构建了 ExecutorService 实现,这些实现由无界作业队列支持。根据频率和运行作业所需的时间,这可能会对系统产生负面影响。在某些情况下,它可能会因为内存中排队的作业数量而导致应用程序宕机。在这些情况下,可能值得使用有界工作队列和自定义 **RejectedExecutionHandler** 创建自己的 **ThreadPoolExecutor**。看到代码部分的重要性,您可能需要无限制版本。请注意影响。 (4认同)

Bex*_*Bex 6

这正是java.util.concurrent.Semaphore设计目的.你这样创建Semaphore:

final int MAX_NOF_THREADS = 5;
final Semaphore mySemaphore = new Semaphore(MAX_NOF_THREADS);
Run Code Online (Sandbox Code Playgroud)

那么你要做的关键领域:

try {
    mySemaphore.aquire(); // This will hang until there is a vacancy
    do_my_critical_stuff();
} finally {
    mySemaphore.release();
}
Run Code Online (Sandbox Code Playgroud)

... 就如此容易.

  • 这段代码可能会产生一个错误:如果线程在等待 aquire() 方法时被中断,它将既不会获取许可证,又会释放一个额外的许可证... (4认同)
  • 我完全同意信号量,他们应该是此任务的首选。不过,有一件事值得一提:从技术上讲,一个线程被允许进行多次收购。这意味着某些逻辑错误(例如,错误的递归)可能导致线程利用不足,甚至导致死锁。但是,如果仔细使用,它们是此处的正确工具。 (2认同)