同一個線程多次調用start()會出現的問題


測試代碼:

 1 package com.led.test;
 2 
 3 /**
 4  * @author Alan
 5  * @date 2018/6/18 15:09
 6  * @description 測試同一個線程多次調用start()方法
 7  */
 8 public class MuchStart implements Runnable{ //實現Runnable接口
 9     public void run() {
10         System.out.println(Thread.currentThread().getName() + "is running ...");
11     }
12 
13     public static void main(String[] args) {
14         MuchStart muchStart = new MuchStart();
15         Thread t = new Thread(muchStart);//創建實現了Runnable接口的Thread實例
16         t.start();//多次調用start()方法
17         t.start();
18         t.start();
19     }
20 }

測試結果:

  線程首先會運行一次,然后拋出java.lang.IllegalThreadStateException異常。

 接下來深入源碼分析下原因:

  我們根據控制台的異常信息,定位到Thread.java的第708行,也就start()方法內部,打個斷點調試:

 

   調試發現,第一個次運行start()方法時,threadStatus是0,此時if條件不滿足,繼續執行,會將當前線程添加到線程組中去執行。第二次運行start()方法時,threadStatus變成了2,if條件滿足,於是拋出了java.lang.IllegalThreadStateException異常。

Thread-0is running ...
Exception in thread "main" java.lang.IllegalThreadStateException
    at java.lang.Thread.start(Thread.java:708)
    at com.led.test.MuchStart.main(MuchStart.java:17)

start()源碼進行分析

 1 /**
 2      * Causes this thread to begin execution; the Java Virtual Machine
 3      * calls the <code>run</code> method of this thread.
     讓這個線程開始執行。JVM會調用這個線程的run()方法。
4 * <p> 5 * The result is that two threads are running concurrently: the 6 * current thread (which returns from the call to the 7 * <code>start</code> method) and the other thread (which executes its 8 * <code>run</code> method). 9 * <p> 10 * It is never legal to start a thread more than once.一個線程多次調用start()方法是非法的。 11 * In particular, a thread may not be restarted once it has completed 12 * execution. 13 *特別說明:一個線程執行完后,不太可能重新運行。 14 * @exception IllegalThreadStateException if the thread was already 15 * started.
              如果該線程已經啟動,則再次調用start()方法,就會拋出IllegalThreadStateException異常。
16 * @see #run() 17 * @see #stop() 18 */ 19 public synchronized void start() { 20 /** 21 * This method is not invoked for the main method thread or "system" 22 * group threads created/set up by the VM. Any new functionality added 23 * to this method in the future may have to also be added to the VM. 24 * 25 * A zero status value corresponds to state "NEW". 26 */ 27 if (threadStatus != 0)//新的線程threadState值是0 28 throw new IllegalThreadStateException(); 29 30 /* Notify the group that this thread is about to be started 31 * so that it can be added to the group's list of threads 32 * and the group's unstarted count can be decremented. */ 33 group.add(this);//通知線程組該線程將要開始運行,這樣該線程就會被添加到線程列表中,此時列表的unstarted數將會減少。 34 35 boolean started = false; 36 try { 37 start0();//調用原生方法態方法啟動線程 38 started = true;//已經運行的標記設置為true 39 } finally { 40 try { 41 if (!started) {//若開始運行標記未設置成功,則通知線程組該線程嘗試運行失敗 42 group.threadStartFailed(this); 43 } 44 } catch (Throwable ignore) { 45 /* do nothing. If start0 threw a Throwable then 46 it will be passed up the call stack */ 47 } 48 } 49 }

group.add(this)  ---線程組添加線程源碼分析

 1 /**
 2      * Adds the specified thread to this thread group.
 3      * 添加特定的線程到該線程組
 4      * <p> Note: This method is called from both library code
 5      * and the Virtual Machine. It is called from VM to add
 6      * certain system threads to the system thread group.
 7      *
 8      * @param  t
 9      *         the Thread to be added
10      *
11      * @throws  IllegalThreadStateException
12      *          if the Thread group has been destroyed
13      */
14     void add(Thread t) {
15         synchronized (this) {//線程同步
16             if (destroyed) {//如果線程組的銷毀標記(destroyed)是true,則拋出IllegalThreadStateException
17                 throw new IllegalThreadStateException();
18             }
19             if (threads == null) {//如果線程數組為空,則初始為4個新的線程
20                 threads = new Thread[4];
21             } else if (nthreads == threads.length) {//如果當前線程組已滿,則擴容至原來的2倍
22                 threads = Arrays.copyOf(threads, nthreads * 2);
23             }
24             threads[nthreads] = t;//將當前線程放入線程數組的末尾
25 
26             // This is done last so it doesn't matter in case the
27             // thread is killed
28             nthreads++;//線程數組元素個數+1
29 
30             // The thread is now a fully fledged member of the group, even
31             // though it may, or may not, have been started yet. It will prevent
32             // the group from being destroyed so the unstarted Threads count is
33             // decremented.
34             nUnstartedThreads--;//未啟動線程數-1
35         }
36     }

 start0()代碼分析:

1 private native void start0();//原生態代碼,源碼不在當前文件中,調用的其它文件的代碼(可能是c/c++寫的代碼)

threadStartFailed()源碼分析:

 1 /**
 2      * Notifies the group that the thread {@code t} has failed
 3      * an attempt to start.
 4      * 通知線程組該線程嘗試運行失敗
 5      * <p> The state of this thread group is rolled back as if the
 6      * attempt to start the thread has never occurred. The thread is again
 7      * considered an unstarted member of the thread group, and a subsequent
 8      * attempt to start the thread is permitted.
 9      *
10      * @param  t
11      *         the Thread whose start method was invoked
12      */
13     void threadStartFailed(Thread t) {
14         synchronized(this) {//同步
15             remove(t);//從線程組中移除該線程
16             nUnstartedThreads++;//未啟動線程數+1
17         }
18     }

 總結:

  同一個線程只能調用start()方法一次,多次調用會拋出java.lang.IllegalThreadStateException。啟動一個線程,需要調用start()方法而不是run()方法。此時,當前線程會被添加到線程組中,進入就緒狀態,等待線程調度器的調用,若獲取到了資源,則能進入運行狀態,run()方法只是線程體,即線程執行的內容,若沒調用start()方法,run()方法只是一個普通的方法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM