我有一个ExecutorService
来同时执行我的任务。这些任务中的大多数都是简单的操作,每个任务需要 ~300 毫秒才能完成。但其中一些任务是后台处理队列,它们始终接收新的子任务并按顺序执行它们。只要有正常任务正在运行,这些后台任务就会保持活动状态。
线程池是通过具有用户指定的线程计数的Executors
' 方法之一(尚不知道是哪个)生成的。我担心可能会发生以下情况:线程数少于后台队列数。在给定时刻,所有后台队列都在工作,从而阻止了ExecutorService
的所有线程。因此,不会启动任何正常任务,程序将永远挂起。
是否有可能发生这种情况,我该如何避免?我正在考虑中断后台任务以将位置留给正常任务的可能性。
目标是限制我的应用程序中的线程数,因为Google说有很多线程是不好的,让它们在大部分时间空闲也是不好的。
在程序执行开始时,将在很短的时间内提交~10000个任务。大约需要 ~50 个后台任务队列,大部分时间将用于等待后台作业。
不要在同一ExecutorService
中将长时间运行的任务与短运行的任务混淆。
使用两个具有正确池大小的不同ExecutorService
实例。即使将具有长时间运行任务的后台线程的大小设置为 50,池的性能也不是最佳的,因为可用内核数(2 核、4 核、8 核等) 不在这个数字中。
我想创建两个单独的ExecutorService
用Runtime.getRuntime().availableProcessors()/2
初始化;
请查看以下帖子,了解有效利用可用内核的更多详细信息:
如何使用固定数量的工作线程实现简单线程
动态线程池
你可以有无限数量的线程,看看缓存线程池
创建一个线程池,该线程池根据需要创建新线程,但将 在以前构造的线程可用时重用它们。这些 池通常会提高执行程序的性能 许多短期异步任务。要执行的调用将重用 以前构造的线程(如果可用)。如果没有现有线程 可用时,将创建一个新线程并将其添加到池中。线程 六十秒未使用的将被终止并删除 从缓存中。因此,保持闲置足够长时间的池将 不消耗任何资源。请注意,具有类似属性但 可能会创建不同的详细信息(例如,超时参数) 使用 ThreadPoolExecutor 构造函数。
另一种选择是创建两个不同的池,并为优先级任务保留一个池。
解决方案是后台任务在没有工作时停止而不是空闲,如果再次有足够的任务,则重新启动。
public class BackgroundQueue implements Runnable {
private final ExecutorService service;
private final Queue<Runnable> tasks = new ConcurrentLinkedQueue<>();
private final AtomicBoolean running = new AtomicBoolean(false);
private Future<?> future;
public BackgroundQueue(ExecutorService service) {
this.service = Objects.requireNonNull(service);
// Create a Future that immediately returns null
FutureTask f = new FutureTask<>(() -> null);
f.run();
future = f;
}
public void awaitQueueTermination() throws InterruptedException, ExecutionException {
do {
future.get();
} while (!tasks.isEmpty() || running.get());
}
public synchronized void submit(Runnable task) {
tasks.add(task);
if (running.compareAndSet(false, true))
future = service.submit(this);
}
@Override
public void run() {
while (!running.compareAndSet(tasks.isEmpty(), false)) {
tasks.remove().run();
}
}
}