java线程重用
我一直认为创build线程很昂贵。 我也知道你不能重新运行一个线程。
我在Executors类的文档中看到:创build一个线程池,根据需要创build新线程,但在可用时会重用之前构build的线程。
注意“重用”一词。
线程池如何“重用”线程?
我想我明白了什么是confuzzabling你,所以这里是我更长的答案:术语是有点误导(显然,或者你不会问这个问题,具体强调'重用'):
线程池如何“重用”线程?
发生了什么事是一个线程可以用来处理几个任务(通常作为Runnable传递,但是这取决于你的“执行者”框架:默认的执行者接受Runnable,但你可以编写你自己的“执行者”/线程池接受一个比Runnable更复杂的东西[比如说,一个CancellableRunnable])。
现在在默认的ExecutorService实现中,如果一个线程在被使用时以某种方式被终止,它将被自动replace为一个新的线程,但这不是他们正在讨论的“重用”。 在这种情况下没有“重用”。
所以确实,你不能在一个Java线程上调用start()
两次, 但是你可以像执行器那样传递尽可能多的Runnable
,并且每个Runnable的run()方法都应该被调用一次。
您可以将30个Runnable
传递给5个Java Thread
并且每个工作线程可能正在调用,例如run()
6次(实际上不能保证每个线程只能执行6个Runnable,但这是一个细节)。
在这个例子中,start()会被调用6次。 每个这6个start()都会调用一次每个Thread的run()
方法:
从Thread.start()Javadoc:
* Causes this thread to begin execution; the Java Virtual Machine * calls the <code>run</code> method of this thread.
但是在每个线程的run()
方法中, Runnable
将被取出,并且每个Runnable
的run()
方法将被调用。 所以每个线程都可以处理好几个Runnable
。 这就是他们所说的“线程重用”。
一种做自己的线程池的方法是使用一个阻塞队列,在这个阻塞队列中可以将可运行队列排入队列中,并且一旦完成处理Runnable
的run()
方法,就让每个线程出列下一个Runnable
(或block)并运行它的run()
方法,然后冲洗并重复。
我猜部分困惑(这有点令人困惑)来自事实上,一个Thread
需要一个Runnable
并在调用start()
时调用Runnable的run()
方法,而默认的线程池也采用Runnable
。
线程池中的线程的run
方法不仅仅由运行单个任务组成。 线程池中线程的run
方法包含一个循环。 它将任务从队列中取出,执行任务(完成时返回到循环 ),然后获取下一个任务。 run
方法不会完成,直到线程不再需要。
编辑添加:
这是ThreadPoolExecutor中 Worker
内部类的run
方法。
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: }
线程池由多个固定的工作线程组成,可以从内部任务队列中获取任务。 所以如果一个任务结束,线程不会结束,而是等待下一个任务。 如果放弃一个线程,它会自动replace。
查看文档以获取更多详细信息。
线程池创build自己的线程,并为这些线程提供自己聪明的小Runnable。 这些Runnables永远不会结束,而是在一个队列上同步(等待()),直到该队列中存在一个Callable。 当发生这种情况时会通知他们,而他们的Runnable从队列中运行Callable,并且整个场景再次重复。
如果thread
完成,确保可以再次使用它。 你可以通过调用isAlive()
或类似的东西来确定它没有运行。
编辑 :如果有人已经暂停一个thread
,一个不能再次启动。 如果正常结束,我不这样做。 但是我怀疑这个好处并且说它不能被重复使用。