在Java中,以下两个代码片段都可用于快速生成新线程以运行某些任务 -
这个使用Thread
-
new Thread(new Runnable() {
@Override
public void run() {
// TODO: Code goes here
}
}).start();
这个使用Executor
-
Executors.newSingleThreadExecutor().execute(new Runnable(){
@Override
public void run() {
// TODO: Code goes here
}
});
在内部,这两个代码之间有什么区别,哪一个是更好的方法? 以防万一,我正在为Android开发。
现在我想,我实际上在寻找newSingleThreadExecutor()
的用例。正是在这个问题中被问到并回答了 -
Examples of when it is convenient to use Executors.newSingleThreadExecutor()
答案 0 :(得分:2)
你的第二个例子很奇怪,创建一个执行器只是为了运行一个任务不是一个好用法。拥有执行程序的目的是让您可以在应用程序期间保留它并向其提交任务。它会起作用,但是你没有得到执行者的好处。
执行程序可以保留一个线程池,方便它可以重用于传入的任务,这样每个任务都不必启动一个新线程,或者如果你选择singleThread,它可以强制执行任务按顺序完成,不重叠。使用执行程序,您可以更好地将正在执行的各个任务与工作方式的技术实现分开。
使用第一种创建线程的方法,如果在某些情况下您的任务出现问题,线程可能会泄露;它会挂起来,永远不会完成它的任务,并且线程会丢失到应用程序以及使用该JVM的任何其他内容。使用执行程序可以为丢失的线程数量设置上限,因此至少应用程序会优雅地降级,并且不会损害使用相同JVM的其他应用程序。
使用线程方法,您创建的每个线程都必须单独跟踪(例如,一旦关闭应用程序,您可以中断它们),执行程序可以关闭执行程序一次,让它自己处理它的线程。
答案 1 :(得分:1)
第二个使用ExecutorService
绝对是最好的方法。
ExecutorService
确定您希望您的任务同时运行的 。它将Runnables
(或Callables
)与其执行分离。
使用Thread
时,您可以将任务与执行方式相结合,从而降低灵活性。
此外,ExecutorService
为您提供了一种更好的方法来跟踪您的任务并使用Future
获取返回值,而来自start
的{{1}}方法只是在没有提供任何信息的情况下运行。 Thread
因此鼓励您在Thread
中编写副作用,这可能会使整体执行难以理解和调试。
同样Runnable
是一种代价高昂的资源,Thread
可以处理其生命周期,重新使用ExecutorService
来运行新任务或根据您定义的策略创建新任务。例如:Thread
只创建一个Executors.newSingleThreadExecutor();
,只有一个线程可以顺序执行传递给它的任务,而ThreadPoolExecutor
创建一个Executors.newFixedThreadPool(8)
,其中包含8个线程,允许最多运行并行8个任务。
答案 2 :(得分:1)
你已经有了三个答案,但我认为这个问题还有一个问题,因为其他人都没有谈论线程池以及它们要解决的问题。
线程池(例如java.util.concurrent.ThreadPoolExecutor
)旨在减少程序创建和销毁的线程数。
某些程序需要不断创建和销毁将在不同线程中运行的新任务。一个示例是服务器接受来自许多客户端的连接,并生成一个新任务来为每个客户端提供服务。
为每个新任务创建一个新线程是昂贵的;在许多程序中,创建线程的成本可能远高于执行任务的成本。它不是让一个线程在完成一个任务后死掉,而是再次使用相同的线程来执行下一个任务会不会更好?
这就是线程池的作用:它管理并重新使用受控数量的工作线程来执行程序的任务。
您的两个示例显示了创建单个线程的两种不同方法,这些方法将执行单个任务,但没有上下文。该任务将执行多少工作?需要多长时间?
第一个例子是一个完全可以接受的方法来创建一个将运行很长时间的线程 - 一个必须在程序的整个生命周期内存在的线程,或者一个执行任务的线程,以至于成本高创建和销毁线程并不重要。
你的第二个例子没有任何意义,因为它创建一个线程池只是为了执行一个Runnable
。为一个Runnable创建一个线程池(或者更糟糕的是,为每个新任务创建一个线程池)完全违背了重用线程的线程池的用途。
java.util.concurrent.ThreadFactory
接口的含义解决。
Google是你的朋友。
答案 3 :(得分:0)
根据ThreadPoolExecutor
线程池解决了两个不同的问题:它们通常提供 在执行大量异步时提高了性能 任务,由于减少了每个任务的调用开销,并且它们提供了一个 绑定和管理资源的方法,包括线程, 在执行任务集合时消耗。每个ThreadPoolExecutor 还保留了一些基本的统计数据,比如已完成的数量 任务。
如果我想生成单个后台处理和小应用程序,第一种方法适合我。
我更喜欢第二种方法来控制线程执行环境。如果我使用ThreadPoolExecutor,我确信1个线程将在时间运行,即使我向执行者提交更多线程。如果您考虑大型企业应用程序,其中线程逻辑不会暴露给其他模块,则往往会发生这种情况。在大型企业应用程序中,您希望控制并发运行的线程数。因此,如果您正在设计企业或大规模应用程序,则第二种方法更具优势。