申明:線程的概念以及進程的相關概念,能夠參考網絡上其餘資料,這裏只討論多線程是怎麼實現。java
1、多線程的簡單理解網絡
明白什麼是多線程,小生通俗一點的理解爲:在一個程序裏,我想同時讓這個程序完成多個任務。多線程
好比:讓主函數 main 在打印1~100之間的全部整數的時候,要求在主函數打印到 20 的時候,再運行另外一個類裏的程序,讓它打印10~100之間的全部整數。jvm
這裏忽略同進程內的多線程之間的搶佔時間問題,上面的舉例需求是要求一個程序只要有發生同時運行倆個程序的狀況就行,即不許出現不管程序跑多少次都是先把 main 函數的1~100 的全部整數打印完再打印10~100 之間的全部整數(PS:若是是這樣的話,那就是 main 調用其餘類的方法,屬於單線程了)。ide
2、建立多線程的三種方式(小生寫的比較繁綴羅嗦,建議閱讀完以後,參考代碼再閱讀一次。)函數
第一種方式:this
建立:編寫一個類 MyThread1 (這裏只是小生的自定義命名,方便與代碼里名稱一致)讓它繼承 Thread 類,編碼
並把須要多線程運行的程序放到 public void run() 方法裏。spa
啓動:在主函數中,new 出 MyThread1 類的實例。線程
運行:調用 MyThread1 類的實例的 start() 方法便可。
第二種方式:
建立:編寫一個類 MyThread2 讓它實現 Runnable 接口,而且要重寫 run() 方法(把須要多線程運行的程序放到 public void run() 方法裏)。
啓動:在主函數中,new 出 MyThread1 類的實例,
new 出Thread 類(帶有 target 的構造方法),
把MyThread1 類的實例做爲參數傳入Thread 類的構造方法裏。
運行:調用 Thread 類的實例的 start() 方法便可。
第三種方式:
建立:實現 Callable 接口(小生定義這個類爲 MyCallable),而且實現 call() 方法,注意 call() 方法是有返回值的。
啓動:new 出Callable 接口的實現類MyCallable,
new 出 FutureTask 類的實例 task,
把call() 方法的返回值放入FutureTask 類的構造方法裏,
把 task 放入 new 出的 Thread 構造方法裏。
運行:調用 Thread 類的實例的 start() 方法便可。
三種方式的利弊總結:
第一種方法好處:建立已經繼承 Thread 類的類的實例,調用 start() 方法就能運行,代碼寫起來很方便,
固然缺點就是繼承了Thread 父類,就沒有辦法繼承其餘的類了,擴展性很差。因此通常不建議用這種方式建立多線程。
第二種方法好處:繼承接口,擴展性好。
弊端是相對的,第一種方式很差也是相對於第二種來講的,而第二種方式相比第三種方式來講,run() 方法沒有返回值,而且不能申明拋出異常。
第三種方式好處上面已經說明,很差的就是編碼很麻煩(小生太笨看了半天才搞明白)
4、建立多線程的三種方式的代碼演示
說明:小生會對每種方式的每條線程進行重命名,只是爲了方便代碼運行效果演示。
java 默認線程的名稱規則:java 中的 main 函數自己就是一個默認的主線程,名字爲:main(在下面例子能夠證明 main 線程在 jvm 中的默認名稱)
若是本身定義的線程沒有定義名稱,則系統默認按照 Thread-0,Thread-1,……的命名方式進行命名。也能夠經過對線程的構造方法進行重命名。
4.1 繼承 Thread 類
偷窺一下 API 發現 Thread 類實現了 Runnable 接口,currentThread() 靜態方法返回當前線程這個實例,再 getName() 一下就獲得當前線程的名字了。
MyThread1 線程:
1 package com.test.threadDemo1; 2 3 public class MyThread1 extends Thread { 4 5 public MyThread1(String name) { 6 this.setName(name); 7 } 8 9 public void run() { 10 for(int i=20;i<=100;i++) { 11 System.out.println(getName()+"..."+i); 12 } 13 } 14 }
main 線程:
1 package com.test.threadDemo1; 2 /** 3 * 多線程的實現之一:繼承 Thread 類 4 * @author Administrator 5 * 6 */ 7 public class ThreadDemo1 { 8 public static void main(String[] args) { 9 for (int i=1;i<=100;i++) { 10 System.out.println(Thread.currentThread().getName()+"..."+i); 11 12 if(i==30) { 13 MyThread1 myThread = new MyThread1("新線程"); 14 myThread.start(); 15 } 16 } 17 } 18 }
運行結果:(有交叉運行的狀況發生,就是多線程了)
從結果能夠看出,當 main 線程跑到 30 的時候還在「搶佔」 CPU
4.2 實現 Runable 接口
MyThread 線程:
1 package com.test.threadDemo1; 2 3 public class MyThread2 implements Runnable { 4 5 public MyThread2(String name) { 6 Thread.currentThread().setName(name); 7 } 8 9 @Override 10 public void run() { 11 for(int i=20;i<=100;i++) { 12 System.out.println(Thread.currentThread().getName()+"..."+i); 13 } 14 } 15 16 }
main 線程:
1 package com.test.threadDemo1; 2 /** 3 * 多線程的實現之二:實現 Runable 接口 4 * @author Administrator 5 * 6 */ 7 public class ThreadDemo2 { 8 public static void main(String[] args) { 9 for (int i=1;i<=100;i++) { 10 System.out.println(Thread.currentThread().getName()+"..."+i); 11 12 if(i==30) { 13 MyThread2 myThread = new MyThread2("新線程"); 14 15 Thread thread = new Thread(myThread); 16 17 thread.start(); 18 } 19 } 20 } 21 }
運行結果:
4.3 實現 Callable 接口和 Future 接口
Future接口的實現類是 FutureTask,它實現了 Runable 接口,因此可以做爲 target 參數傳到 Thread 類的構造方法裏。
FutureTask(Callable<V> callable)
從構造方法裏能夠看出 FutureTask 的構造方法裏傳入 callable 的實現類就好了。
MyThread3 線程:
1 package com.test.threadDemo1; 2 3 import java.util.concurrent.Callable; 4 5 public class MyThread3 implements Callable<Integer> { 6 7 @Override 8 public Integer call() throws Exception { 9 int i = 20; 10 for(;i<=100;i++) { 11 System.out.println(Thread.currentThread().getName()+"..."+i); 12 } 13 return i; 14 } 15 16 }
main 線程:
1 package com.test.threadDemo1; 2 3 import java.util.concurrent.Callable; 4 import java.util.concurrent.FutureTask; 5 6 /** 7 * 多線程的實現之三:實現 Callable 接口和 Future 接口 8 * @author Administrator 9 * 10 */ 11 public class ThreadDemo3 { 12 public static void main(String[] args) { 13 14 Callable<Integer> myThread3 = new MyThread3(); 15 FutureTask<Integer> task = new FutureTask<Integer>(myThread3); 16 17 for (int i=1;i<=100;i++) { 18 System.out.println(Thread.currentThread().getName()+"..."+i); 19 20 if(i==30) { 21 22 Thread thread = new Thread(task,"新線程"); 23 thread.start(); 24 } 25 } 26 } 27 }
運行結果: