Java之多線程之線程池之線程重複使用

[size=medium][b]一、問題背景[/b][/size]

在使用多線程時,如果要開啓一個任務,則就需要新建一個線程。
線程也是對象,那麼是否可以不用新建,而使用原來的呢?

試試下面的方法:



Thread incT = new Thread(new Inc(c));
Thread decT = new Thread(new Dec(c));


for(int i= 0; i < 200; i++){
incT.start();
System.out.println("incT:" + incT.getName());
decT.start();
System.out.println("decT:" + decT.getName());
}



結果報錯了:
Exception in thread "main" java.lang.IllegalThreadStateException

原因:
線程在執行完 start() 方法後,就會自動銷燬。

[size=medium][b]二、如何解決線程重用[/b][/size]

[b]1、分析[/b]
每一個 Thread 的類都有一個 start 方法。
Thread 的 start 方法是這樣描述的:

Causes this thread to begin execution;
the Java Virtual Machine calls the run method of this thread.
啓動線程。Java虛擬機會調用該類的 run 方法。

那麼該類的 run() 方法中就是調用了 Runnable 對象的 run() 方法。

[b]2、解決[/b]
我們可以繼承重寫 Thread 類,在其 start 方法中添加不斷循環調用傳遞過來的 Runnable 對象。
這就是線程池的實現原理。循環方法中不斷獲取 Runnable 是用 Queue 實現的,在獲取下一個 Runnable 之前可以是阻塞的。


[size=medium][b]三、問題出處[/b][/size]

[b]Question: java thread reuse[/b]

I have always read that creating threads is expensive. I also know that you cannot rerun a thread.

I see in the doc of Executors class: Creates a thread pool that creates new threads as needed, but will reuse previously constructed threads when they are available.

Mind the word 'reuse'.

How do thread pools 'reuse' threads?


------------
Answer:

I think I understood what is confuzzabling you so here's my longer answer: the terminology is a tiny bit misleading (obviously, or you wouldn't ask that question specifically putting the emphasis on 'reuse'):

How do thread pools 'reuse' threads?

What is happening is that a single thread can be used to process several tasks (typically passed as Runnable, but this depend on your 'executor' framework: the default executors accepts Runnable, but you could write your own "executor" / thread-pool accepting something more complex than a Runnable [like, say, a CancellableRunnable]).

Now in the default ExecutorService implementation if a thread is somehow terminated while still in use, it is automatically replaced with a new thread, but this is not the 'reuse' they're talking about. There is no "reuse" in this case.

So it is true that you cannot call start() on a Java Thread twice but you can pass as many Runnable as you want to an executor and each Runnable's run() method shall be called once.

You can pass 30 Runnable to 5 Java Thread and each worker thread may be calling, for example, run() 6 times (practically there's not guarantee that you'll be executing exactly 6 Runnable per Thread but that is a detail).

In this example start() would have been called 6 times. Each one these 6 start() will call exactly once the run() method of each Thread:

From Thread.start() Javadoc:

* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.

BUT then inside each Thread's run() method Runnable shall be dequeued and the run() method of each Runnable is going to be called. So each thread can process several Runnable. That's what they refer to by "thread reuse".

One way to do your own thread pool is to use a blocking queue on to which you enqueue runnables and have each of your thread, once it's done processing the run() method of a Runnable, dequeue the next Runnable (or block) and run its run() method, then rinse and repeat.

I guess part of the confusion (and it is a bit confusing) comes from the fact that a Thread takes a Runnable and upon calling start() the Runnable 's run() method is called while the default thread pools also take Runnable.


-
Thread pool threads are basically running loops that pull submitted tasks off of a queue. The threads do not stop executing when they service a task, they just wait for the next one to be submitted to the queue. They never get 'rerun' as asked in the question, as they are just constantly running.

-


The run method of threads in a thread pool does not consist only of running a single task.
The run method of a thread in a thread pool contains a loop.
[b]It pulls a task off of a queue, executes the task (which returns back to the loop when it is complete), and then gets the next task.
[/b]
The run method doesn't complete until the thread is no longer needed.

Here is the run method of the Worker inner class in ThreadPoolExecutor.


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: }


-

It's not actually unnecessary - the (strange) test for task!=null in the loop makes it necessary to prevent continually processing the same task. Even if the loop were more conventional nulling task would be good because otherwise if getTask() blocks for a long time, the GC of task would otherwise be delayed for the same length of time.


-


-
http://stackoverflow.com/questions/2324030/java-thread-reuse
發佈了279 篇原創文章 · 獲贊 4 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章