我能在线程池中运行后台任务吗?

3
我有一个ExecutorService来并发地执行我的任务。其中大部分任务是简单操作,每个操作需要约300毫秒完成。但是其中有一些任务是背景处理队列,它们一直接收并按顺序执行新的子任务。只要有普通任务在运行,这些后台任务就会保持活动状态。
线程池是通过Executors的方法(还不知道是哪个)生成的,该方法允许用户指定线程数。我担心以下情况可能会发生:线程数少于后台队列数。在某个时刻,所有后台队列都在工作,阻塞了ExecutorService的所有线程。因此,没有普通任务将被启动,程序将永久挂起。
这种情况是否可能发生,如何避免?我正在考虑中断后台任务,以为普通任务腾出位置。
目标是限制应用程序中的线程数量,因为Google表示拥有大量线程不好,而它们大多数时间都处于空闲状态也不好。
在程序执行开始时,将提交约10000个任务。大约需要~50个后台任务队列,并且大部分时间将花费在等待后台作业进行。

为什么不只是创建具有自己的线程池的单独执行器呢? - Justas
这样做会为每个任务创建一个线程吗?我最后会有成千上万个线程... - piegames
一个执行器服务用于后台任务,另一个用于常规任务 - 在这种情况下,它们不会竞争线程。这样,用户指定的线程计数可以用于正常任务执行器池。 - Justas
3个回答

2
不要在同一个ExecutorService中混合长时间运行的任务和短时间运行的任务。使用两个不同的ExecutorService实例,并设置正确的池大小。即使您为具有长时间运行任务的后台线程设置了50个大小,池的性能也不是最佳的,因为可用核心数(2核、4核、8核等)不在该数字中。
我想创建两个单独的ExecutorService,并使用Runtime.getRuntime().availableProcessors()/2进行初始化;
请参阅以下帖子以获取更多详细信息,以有效利用可用核心: 如何使用固定数量的工作线程实现简单线程 动态线程池

1

您可以拥有无限数量的线程,请查看缓存线程池

创建一个线程池,根据需要创建新线程,但会在可用时重复使用先前构建的线程。这些线程池通常会提高执行许多短暂异步任务的程序的性能。如果有可用的现有线程,则调用execute将重用先前构造的线程。如果没有现有线程可用,则会创建一个新线程并将其添加到池中。未使用的线程将在六十秒后终止并从缓存中删除。因此,长时间处于闲置状态的池不会消耗任何资源。请注意,可以使用ThreadPoolExecutor构造函数创建具有类似属性但不同细节(例如超时参数)的池。

另一种选择是创建两个不同的池,并将其中一个保留为优先任务。


0

解决方案是当没有任务时,后台任务停止而不是空闲,并在有足够的任务时重新启动。

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

加上一些评论会让其他人更清楚地理解。 - Gary Chen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接