線程的運行比較復雜,平常我們調用start(start0;)方法就完事了,啥時候執行run里面的代碼?經過各種狀態的轉換獲得cpu時間片,jvm就會幫我們執行run方法,執行完run方法這個線程自動消亡,遇到異常線程也會消亡,這就是一個線程的生命周期。
線程有兩種實現方式,一種是繼承Thread,重寫run方法,一種是自己寫一個Task實現runable接口重寫run方法,他們的啟動方式 分別是如下
//第一種方式,繼承Thread,重寫run方法后的啟動 new MyThread().start(); //第二種方式,將實現的runable接口的task作為參數傳入Thread構造方法 new Thread(new Runnable() { @Override public void run(){ System.out.println("do something"); } }).start();
jvm執行完run方法,就會執行退出方法,該退出方法在jvm內部用C++語言寫的
void JavaThread::exit(bool destroy_vm, ExitType exit_type) { //線程退出的方法 }
我們看這兩種線程啟動方式,感覺啟動一次只能傳一個任務進去。而且run方法又是由黑盒的jvm執行的,jvm執行完run方法就退出了,那線程池是怎么傳多個任務進去的?怎么控制run方法一直執行的?
這一系列疑問,要進入線程池源碼才能略知一二
源碼實在太復雜了,我們從前文的鋪墊開始,以正常人的思路,首先找到我們的主角---線程
1,線程池中的線程在哪?
當我們沒有將線程工廠傳入的時候使用的是默認的線程工廠
//1,ThreadPoolExecutor的構造方法眾多,但都指向一個,那就是如下 this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), handler); //2,將Executors.defaultThreadFactory()進入會發現默認線程工廠 public static ThreadFactory defaultThreadFactory() { return new DefaultThreadFactory(); } //3,在哪觸發產生線程?在如下構造方法里, //產生線程的s時候將這個內部類this本身傳遞進去 Worker(Runnable firstTask) { setState(-1); // inhibit interrupts until runWorker this.firstTask = firstTask; this.thread = getThreadFactory().newThread(this); } //4,進入產生線程的方法 public Thread newThread(Runnable r) { Thread t = new Thread(group, r,namePrefix + threadNumber.getAndIncrement(),0); ...//其他代碼 return t; }
好了我們的線程主角已經登場,但是他的登場並沒有多高大上,而是寄人籬下在一個名叫Worker的內部類里
這個意思是只要 new Worker(Runnable firstTask),就會產生一個線程,並且產生線程的時候將這個內部類本身this傳入進去當task。
這似乎跟我們前文講的差不多,new一個線程就放一個task,說好的線程復用呢?好了主角線程有了,我們的task也有了,我們再找找start方法
//線程啟動的時候 private boolean addWorker(Runnable firstTask, boolean core) { w = new Worker(firstTask); final Thread t = w.thread; ...//代碼 t.start(); ...//代碼 }
線程是啟動了,別忘了,線程啟動的時候我們將內部類worker對象傳入進去了,內部類Worker是實現了runable接口的,jvm執行run方法的時候就會執行Worker中的run方法
//1,這是Work中的一個方法,線程啟動的時候jvm會執行它, public void run() { runWorker(this); } //2,繼續看里面的runWorker(this);很顯然this是內部類Work對象本身 final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null; ../代碼 try { while (task != null || (task = getTask()) != null) { try{ ../代碼 task.run(); }catch(){ ../代碼 } finally { ../代碼 } } ../代碼 } finally { ../代碼 } }
很顯然這個runWorker很不得了,里面一個大大的while循環,當我們的task不為空的時候它就永遠在循環,並且會源源不斷的從getTask()獲取新的任務,繼續看getTask()方法
//很顯然這個方法是從隊列中獲取任務workQueue private Runnable getTask() { ...//代碼 // Are workers subject to culling? boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) return null; continue; } try { Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) return r; timedOut = true; } catch (InterruptedException retry) { timedOut = false; } } }
很顯然,getTask()方法里面有個三元表達式,當條件為真時從任務對列workQueue.take()里面獲取要執行得任務;
講到這里線程復用的流程就講完了,最核心的一點是,新建一個Worker內部類就會建一個線程,並且會把這個內部類本身傳進去當作任務去執行,這個內部類的run方法里實現了一個while循環,當任務隊列沒有任務時結束這個循環,則這個線程就結束。