2016-07-24 28 views
4

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

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

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

+1

为什么不能使用java.util.concurrent.Semaphore? –

回答

4

这正是java.util.concurrent.Semaphore设计做。您创建一个Semaphore像这样:

final int MAX_NOF_THREADS = 5; 
final Semaphore mySemaphore = new Semaphore(MAX_NOF_THREADS); 

那么对于关键领域,你会怎么做:

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

...就这么简单。

+1

我完全同意'Semaphore's,他们应该是这项任务的首选。不过,有一点值得一提:从技术上讲,允许一个线程进行多次获取。这意味着一些逻辑错误(例如错误的递归)可能导致线程利用率不足甚至导致死锁。但如果使用得当,它们就是这里的正确工具。 –

4

虽然,Semaphore是最好的选择(请看@Bex的答案)如果你小心,也可以使用ExecutorService。只是包装的一段代码,你想从无限的并发访问,以保护为Callable任务,并提交这样的任务执行人服务:

// 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()); 

随着ExecutorService你也可以使用Runnable代替CallableinvokeAll()代替execute,等待任务完成,取消任务,返回值并做一些其他有用的事情。

的Java 8使它更简单,你可以使用lambda表达式而不是定义任务类:

executor.submit(() -> { 
    // Do the work here 
}); 
+1

需要记住的一点是,Executor工厂将构建由无界作业队列支持的ExecutorService实现。根据频率和运行作业的时间长短,这可能会对系统产生负面影响。在某些情况下,由于在内存中排队的作业数量有限,它可能会降低应用程序的性能。在这些情况下,可能值得创建自己的** ThreadPoolExecutor **,其中包含一个有界的工作队列和一个自定义的** RejectedExecutionHandler **。看到这段代码如何是关键的,你可能需要无限制的版本。请注意影响。 – Jeremiah

相关问题