首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >重新启动线程比创建新线程更好吗?

重新启动线程比创建新线程更好吗?
EN

Stack Overflow用户
提问于 2022-07-31 18:20:18
回答 2查看 70关注 0票数 1

我想知道在执行对象的过程中保持相同的线程是否有什么好处,而不是重复使用相同的线程对象。我有一个对象,它使用本地线程变量并行化单个(经常使用的)方法,这样每次调用该方法时,都会实例化新线程(和Runnable)。由于该方法被频繁调用,一次执行可能实例化超过10万个Thread对象,即使在任何给定时间都没有超过几个(~4-6)活动。

下面是一个简化的示例,说明该方法目前是如何实现的,以给出我的意思。作为参考,n当然是要使用的预先确定的线程数,而this.dataStructure是一个(线程安全的) Map,它充当计算的输入,并且被计算所修改。还有其他的输入,但由于它们与这个问题无关,所以我省略了它们的用法。出于同样的原因,我也省略了异常处理。

代码语言:javascript
复制
Runnable[] tasks = new Runnable[n];
Thread[] threads = new Thread[n];
ArrayBlockingQueue<MyObject> inputs = new ArrayBlockingQueue<>(this.dataStructure.size());
inputs.addAll(this.dataStructure.values());

for (int i = 0; i < n; i++) {
    tasks[i] = () -> {
        while (true) {
            MyObject input = inputs.poll(1L, TimeUnit.MICROSECONDS);
            if (input == null) return;
            // run computations over this.dataStructure
        }
    };
    threads[i] = new Thread(tasks[i]);
    threads[i].start();
}

for (int i = 0; i < n; i++)
    threads[i].join();

因为这些线程(以及它们的可运行项)总是使用单个ArrayBlockingQueue作为输入执行相同的方式,所以替代方法就是每次调用方法时“重新填充队列”,然后重新启动相同的线程。这是很容易实现的,但我不确定这样做会不会有任何不同。我不太熟悉并发,所以非常感谢您的帮助。

PS.:如果有一种更优雅的方法来处理投票,那也是有帮助的。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-07-31 19:45:14

不可能启动线程不止一次,但从概念上说,您的问题的答案是肯定的。

这通常是通过线程池完成的。线程池是一组很少实际终止的线程。相反,应用程序将其任务传递给线程池,线程池将在线程池中选择运行线程。然后,线程池决定是否应该在任务完成后终止或重用线程。

Java有一些非常容易使用线程池的类:ExecutorServiceCompletableFuture

ExecutorService的使用通常如下所示:

代码语言:javascript
复制
ExecutorService executor = Executors.newCachedThreadPool();

for (int i = 0; i < n; i++) {
    tasks[i] = () -> {
        while (true) {
            MyObject input = inputs.poll(1L, TimeUnit.MICROSECONDS);
            if (input == null) return;
            // run computations over this.dataStructure
        }
    };
    executor.submit(tasks[i]);
}

// Doesn't interrupt or halt any tasks.  Will wait for them all to finish
// before terminating its threads.
executor.shutdown();

executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);

Executors还有其他可以创建线程池的方法,比如newFixedThreadPool()newWorkStealingPool()。你可以自己决定哪一个最适合你的需要。

CompletableFuture的使用可能如下所示:

代码语言:javascript
复制
Runnable[] tasks = new Runnable[n];
CompletableFuture<?>[] futures = new CompletableFuture<?>[n];

for (int i = 0; i < n; i++) {
    tasks[i] = () -> {
        while (true) {
            MyObject input = inputs.poll(1L, TimeUnit.MICROSECONDS);
            if (input == null) return;
            // run computations over this.dataStructure
        }
    };
    futures[i] = CompletableFuture.runAsync(tasks[i]);
}

CompletableFuture.allOf(futures).get();

CompletableFuture的缺点是任务不能被取消或中断。(调用cancel会将任务标记为异常完成,而不是成功完成,但任务不会被中断。)

票数 3
EN

Stack Overflow用户

发布于 2022-07-31 19:35:55

根据定义,不能重新启动线程。根据文档:不止一次启动线程是合法的。特别是,线程一旦完成执行就不能重新启动。

然而,线程是一个有价值的资源,还有一些实现可以重用线程。看一看关于执行器的Java教程

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73185882

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档