我一直认为创建线程很贵 我也知道你不能重新运行一个帖子。
我在Executors
课程的文档中看到了:
创建一个根据需要创建新线程的线程池,但在它们可用时将重用以前构造的线程。
注意'重复'这个词。
线程池如何'重用'线程?
答案 0 :(得分:48)
我想我明白了什么让你困惑所以这里是我的答案:这个术语有点误导(显然,或者你不会特别强调'重用'这个问题):
线程池如何“重用”线程?
发生的事情是单个线程可用于处理多个任务(通常以Runnable
传递,但这取决于您的'执行者'框架:默认执行者接受Runnable
,但是您可以写自己的“执行者”/线程池接受比Runnable
更复杂的东西[比如说,CancellableRunnable
]。
现在在默认的ExecutorService
实现中,如果一个线程在仍然在使用时以某种方式被终止,它将自动被一个新线程替换,但这不是他们所讨论的'重用'。在这种情况下没有“重用”。
所以你不能两次在Java线程上调用start()
但是你可以将任意数量的Runnable
传递给执行者,每个{{1} } Runnable
方法应该被调用一次。
您可以将30 run()
传递给5个Java Runnable
,并且每个工作线程可能正在调用,例如Thread
6次(实际上并不保证您将完全执行每run()
6 Runnable
,但这是一个细节。
在此示例中,Thread
将被调用6次。这6个start()
中的每一个都会调用一次每个start()
的{{1}}方法:
来自run()
Javadoc:
Thread
但是然后在每个线程的Thread.start()
方法 * Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
内部将被出列,并且将调用每个run()
的{{1}}方法。因此每个线程可以处理多个Runnable
。这就是他们所谓的“线程重用”。
执行自己的线程池的一种方法是使用一个阻塞队列,在这个阻塞队列中对runnables进行排队并让每个线程在处理run()
的{{1}}方法后出列,然后出列下一个Runnable
(或阻止)并运行其Runnable
方法,然后冲洗并重复。
我想部分混乱(而且有点混乱)来自于run()
需要Runnable
并且在调用Runnable
Runnable的{{1}时调用方法时,默认线程池也取run()
。
答案 1 :(得分:14)
线程池中的run
线程方法不仅包含运行单个任务。线程池中线程的run
方法包含一个循环。它从队列中拉出任务,执行任务(当完成时将返回到循环),然后获取下一个任务。在不再需要该线程之前,run
方法不会完成。
编辑添加:
以下是ThreadPoolExecutor中run
内部类的Worker
方法。
696: /**
697: * Main run loop
698: */
699: public void run() {
700: try {
701: Runnable task = firstTask;
702: firstTask = null;
703: while (task != null || (task = getTask()) != null) {
704: runTask(task);
705: task = null; // unnecessary but can help GC
706: }
707: } finally {
708: workerDone(this);
709: }
710: }
答案 2 :(得分:5)
线程池由许多固定工作线程组成,这些线程可以从内部任务队列中获取任务。因此,如果一个任务结束,则线程不结束,但等待下一个任务。如果中止线程,则会自动替换它。
查看documentation了解更多详情。
答案 3 :(得分:1)
线程池创建自己的线程并为这些线程提供自己聪明的小Runnables。那些Runnables永远不会结束但是在队列上同步(等待())直到该队列中存在Callable;当发生这种情况时会通知他们,并且他们的Runnable从队列中运行Callable,整个场景再次重复。