本文經過幾個問題入手,主要經過案例講解Thread類的使用,以及注意事項,最後站在源碼的角度分析問題。java
多線程不必定快!多線程
多線程只是極大限度的利用CPU的空閒時間來處理其餘的任務,這樣纔會縮短多個任務的執行時間。若是在沒有CPU空閒的狀況下,多線程之間的上下文切換(保存線程的運行環境,還原線程的運行環境)會產生開銷,執行時間會慢於單線程。異步
單任務操做系統:同步執行,排隊。只有當前任務執行完了才能夠執行下一個任務。例如cmd中執行完一條指令以後才能夠執行另外一條指令ide
多任務操做系統:異步執行,經過時間片的快速切換實現多個任務響應執行。源碼分析
(1)繼承Thread類,重寫run( )方法
測試
package com.feng.example; public class MyThread extends Thread { @Override public void run() { // TODO Auto-generated method stub super.run(); System.out.println("繼承Thread類"); } }
啓動線程:spa
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub MyThread myThread = new MyThread(); myThread.start(); } }
(2)建立一個Runnable接口的實現類A,將A的實例對象做爲Thread類的參數傳入操作系統
package com.feng.example; public class MyRunnable implements Runnable { @Override public void run() { // TODO Auto-generated method stub System.out.println("經過實現Runnable接口重寫run"); } }
啓動線程:線程
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Runnable myRunnable = new MyRunnable(); Thread myThread = new Thread(myRunnable); myThread.start(); } }
因爲在Thread類也是實現Runnable接口的,所以Thread的對象也能夠做爲第二種形式的參數code
public class Thread implements Runnable {} //這裏只是貼出源碼中Thread類的定義
將第一種方式建立的MyThread類對象做爲第二種方式的輸入參數
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub //建立MyThread類對象 Runnable myRunnable = new MyThread(); Thread myThread = new Thread(myRunnable); myThread.start(); } }
兩種方式的區別:兩種方式沒有本質的區別,通常都會使用第二種形式,主要解決java中類不能多繼承的缺陷。
好比Cat類繼承Animal,同時又想將Cat定義爲線程類,由於Cat不能同時繼承Animal,Thread類,可是Cat能夠繼承Animal,實現Runnable接口
回顧一下操做系統課中線程的建立過程:線程的建立包括兩部分
(1)建立線程,至關於MyThread myThread = new MyThread( );
(2)將線程放到就緒隊列中,若是僅僅只是建立了線程卻沒有將線程放到就緒隊列中,線程調度器是無法找到線程的。所以myThread.start( );將線程放到就緒隊列中。等待分配cpu時間片,當獲取了cpu以後就能夠執行線程的run( )方法
經過start( )方法:是新開闢了一個線程,run( )方法是由線程規劃器來調用的,是異步運行
直接調用run( )方法:調用者是Thread子類的一個對象,屬於同步執行,沒有開闢新的線程。
package com.feng.example; public class MyThread extends Thread { @Override public void run() { // TODO Auto-generated method stub super.run(); System.out.println("線程爲:"+Thread.currentThread().getName()); } }
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub //建立MyThread類對象 MyThread myThread = new MyThread(); myThread.start(); myThread.run(); //同步調用run方法 } }
程序運行結果:
首先查看Thread類源碼中是如何重寫Runnable接口中的run( )方法的。
public void run() { if (target != null) { target.run(); } }
接下來看一下這個target又是什麼?
/* What will be run. */ private Runnable target;
由源碼能夠看出,在Thread類的內部,存在一個類型爲Runnable的成員變量,此成員變量即是接收第二種建立線程類的方法中的輸入參數的。再看Thread類的run方法,只是判斷有此target存不存在,若是存在便執行target的run( )方法。
若是不存在怎麼辦?
若是不存在則是使用繼承的方式來實現的線程類,那麼run( )方法已經在Thread子類中進行了重寫,覆蓋了父類的run( )方法
下面經過一個題目來消化這個知識點:
Runnable接口的實現類以下:
package com.feng.example; public class MyRunnable implements Runnable { @Override public void run() { // TODO Auto-generated method stub System.out.println("經過Runnable接口實現線程類"); } }
Thread的子類實現以下:
package com.feng.example; public class MyThread extends Thread { public MyThread(Runnable target) { super(target); } @Override public void run() { // TODO Auto-generated method stub System.out.println("經過繼承實現線程類"); } }
測試代碼以下:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub //建立Runnable接口實現類 Runnable myRunnable = new MyRunnable(); //建立MyThread類對象 MyThread myThread = new MyThread(myRunnable); //到底輸出的是誰的run方法???? myThread.start(); } }
分析: 首先start的是MyThread線程,由於會執行MyThread類的run( )方法。雖然傳入了Runnable類型的參數,可是在MyThread類中的run( )方法中並無super.run( );所以程序只輸出MyThread類中的run( )方法中的輸出。
輸出結果如圖:
修改MyThread類中的run( )方法:
package com.feng.example; public class MyThread extends Thread { public MyThread(Runnable target) { super(target); } @Override public void run() { // TODO Auto-generated method stub super.run(); System.out.println("經過繼承實現線程類"); } }
分析:
首先start的是MyThread線程,由於會執行MyThread類的run( )方法。由於在構造MyThread時傳入了Runnable類型的參數,所以在執行MyThread類中的run( )方法時,先執行super.run( ); 執行Thread類的run( ); 根據源代碼先檢查target存不存,這裏target就是存入的myRunnable, 因此執行myRunnable裏面的run( );執行完super.run( )方法後,在輸出後面的語句。
輸入結果以下圖:
在有些地方可能會以匿名類的形式來進行考察:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { new Thread(new Runnable(){ @Override public void run() { // TODO Auto-generated method stub System.out.println("Runnable..."); } }){ public void run() { //super.run(); System.out.println("Thread..."); } }.start(); } }
看到此代碼不要驚慌,其實與我最初定義的三個類文件的考察方式是同樣的,只不過這裏使用了匿名類,若是忘記了回去翻看匿名類的知識,此題注意註釋的那一句super.run( ); 看一下注釋不註釋有什麼區別。
只要理解Thread源碼中的run( )方法,這個題目就顯得很簡單了。