Java多線程實現方式主要有三種:繼承Thread類、實現Runnable接 口、使用ExecutorService、Callable 實現有返回結果的多線程。其中前兩種方式線程執行完後都沒有返回值,只有最後一種Callable是帶返回值的,返回結果能夠從Future中取出來html
關於ExecutorService 參考:Java-線程池專題 (美團)java
一、繼承Thread類實現多線程
繼承Thread類的方法儘管被我列爲一種多線程實現方式,但Thread本質上也是實現了Runnable接口的一個實例,它表明一個線程的實例,而且,啓動線程的惟一方法就是經過Thread類的start()實例方法。start()方法是一個native方法,它將啓動一個新線程,並執行run()方法。這種方式實現多線程很簡單,經過本身的類直接extend Thread,並複寫run()方法,就能夠啓動新線程並執行本身定義的run()方法。例如:編程
public class MyThread extends Thread { public void run() { System.out.println("MyThread.run()"); } }
在合適的地方啓動線程以下:緩存
MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); myThread1.start(); myThread2.start();
二、實現Runnable接口方式實現多線程多線程
若是本身的類已經extends另外一個類,就沒法直接extends Thread,此時,必須實現一個Runnable接口,以下:併發
public class MyThread extends OtherClass implements Runnable { public void run() { System.out.println("MyThread.run()"); } }
爲了啓動MyThread,須要首先實例化一個Thread,並傳入本身的MyThread實例:框架
public void run() { if (target != null) { target.run(); } }
三、使用ExecutorService、Callable 實現有返回結果的多線程ide
ExecutorService、Callable 這個對象實際上都是屬於Executor框架中的功能類。想要詳細瞭解Executor框架的能夠訪問 主題:java併發編程-Executor框架,這裏面對該框架作了很詳細的解釋。返回結果的線程是在JDK1.5中引入的新特徵,確實很實用,有了這種特徵我就不須要再爲了獲得返回值而大費周折了,並且即使實現了也可能漏洞百出。
可返回值的任務必須實現Callable接口,相似的,無返回值的任務必須Runnable接口。執行Callable任務後,能夠獲取一個Future的對象,在該對象上調用get就能夠獲取到Callable任務返回的Object了,再結合線程池接口ExecutorService就能夠實現傳說中有返回結果的多線程了。下面提供了一個完整的有返回結果的多線程測試例子,在JDK1.5下驗證過沒問題能夠直接使用。代碼以下:post
import java.util.concurrent.*; import java.util.Date; import java.util.List; import java.util.ArrayList; /** * 有返回值的線程 */ @SuppressWarnings("unchecked") public class Test { public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("----程序開始運行----"); Date date1 = new Date(); int taskSize = 5; // 建立一個線程池 ExecutorService pool = Executors.newFixedThreadPool(taskSize); // 建立多個有返回值的任務 List<Future> list = new ArrayList<Future>(); for (int i = 0; i < taskSize; i++) { Callable c = new MyCallable(i + " "); // 執行任務並獲取Future對象 Future f = pool.submit(c); // System.out.println(">>>" + f.get().toString()); list.add(f); } // 關閉線程池 pool.shutdown(); // 獲取全部併發任務的運行結果 for (Future f : list) { // 從Future對象上獲取任務的返回值,並輸出到控制檯 System.out.println(">>>" + f.get().toString()); } Date date2 = new Date(); System.out.println("----程序結束運行----,程序運行時間【" + (date2.getTime() - date1.getTime()) + "毫秒】"); } } class MyCallable implements Callable<Object> { private String taskNum; MyCallable(String taskNum) { this.taskNum = taskNum; } public Object call() throws Exception { System.out.println(">>>" + taskNum + "任務啓動"); Date dateTmp1 = new Date(); Thread.sleep(1000); Date dateTmp2 = new Date(); long time = dateTmp2.getTime() - dateTmp1.getTime(); System.out.println(">>>" + taskNum + "任務終止"); return taskNum + "任務返回運行結果,當前任務時間【" + time + "毫秒】"; } }
代碼說明:
上述代碼中Executors類,提供了一系列工廠方法用於創先線程池,返回的線程池都實現了ExecutorService接口。
public static ExecutorService newFixedThreadPool(int nThreads)
建立固定數目線程的線程池。
public static ExecutorService newCachedThreadPool()
建立一個可緩存的線程池,調用execute 將重用之前構造的線程(若是線程可用)。若是現有線程沒有可用的,則建立一個新線程並添加到池中。終止並從緩存中移除那些已有 60 秒鐘未被使用的線程。
public static ExecutorService newSingleThreadExecutor()
建立一個單線程化的Executor。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
建立一個支持定時及週期性的任務執行的線程池,多數狀況下可用來替代Timer類。
ExecutoreService提供了submit()方法,傳遞一個Callable,或Runnable,返回Future。若是Executor後臺線程池尚未完成Callable的計算,這調用返回Future對象的get()方法,會阻塞直到計算完成。
具體 ExecutorService的使用 參考:Java-線程池專題 (美團)
若是研究一下源碼就會發現:Thread其實自己就是實現了接口 Runnable的一個類;
所以 Thread中的方法和成員變量要比Runnable多,最典型地就是 Thread有start()方法,可是Runnable接口沒有start()方法;
若是想要執行一段線程:
public threadTest extends Thread {... @Override public void run() {..} } threadTest th1=new threadTest("一號窗口"); th1.start();
這裏 繼承了Thread類,而且重寫了方法run();
而後調用 此類的start()方法來執行,記住不是調用run()方法執行 ,而是start(),由於:
1.run()並非啓動線程,而是簡單的方法調用。
2.並非一啓動線程(調用start()方法)就執行這個線程,而是進入就緒狀態,何時運行要看CPU。
實際開發中咱們一般採用Runnable接口來實現多線程。由於實現Runnable接口比繼承Thread類有以下好處:
1. 避免繼承的侷限,一個類能夠繼承多個接口,可是類只能繼承一個類。
2. Runnable接口實現的線程便於資源共享。而經過Thread類實現,各自線程的資源是獨立的,不方便共享。
public class Thread extends Object implements Runnable
發現Thread類也是Runnable接口的子類。
針對於區別的第二條:Runnable接口實現的線程便於資源共享,而經過Thread類實現,各自的線程的資源是獨立的,不方便共享。
舉個栗子:
網上最經典的賣票的例子:
(1)繼承Thread類:
package com.thread; /* * 經過繼承Thread類,實現多線程 * Thread類是有run()方法的; * 也有start方法 * */ public class threadTest extends Thread { private int ticket =10; private String name; public threadTest(String name){ this.name=name; } @Override public void run() { // TODO Auto-generated method stub // super.run(); while(true){ if(this.ticket>0){ try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(this.name+"賣票--->"+(this.ticket--)); }else{ break; } } } public static void main(String[] args) { // TODO Auto-generated method stub threadTest th1=new threadTest("一號窗口"); threadTest th2=new threadTest("二號窗口"); th1.start(); th2.start(); // threadTest mt=new threadTest(); // Thread t1 =new Thread(mt,"一號窗口"); // Thread t2 =new Thread(mt,"二號窗口"); // t1.start(); // t2.start(); } }
(2)實現Runnable接口:
package com.thread; /* * 經過實現Runnable接口,實現多線程 * Runnable類是有run()方法的; * 可是沒有start方法 * */ public class runnableTest implements Runnable { private int ticket =10; @Override public void run() { // TODO Auto-generated method stub while(true){ if(ticket>0){ try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"賣票--->"+(this.ticket--)); }else{ break; } } } public static void main(String[] args) { runnableTest mt=new runnableTest(); Thread t1 =new Thread(mt,"一號窗口"); Thread t2 =new Thread(mt,"二號窗口"); t1.start(); t2.start(); } }
能夠看到:Thread 各線程資源是獨立的,Runnable 便於實現線程共享資源;
Runable Callable Future都是咱們在java多線程開發中遇到的接口,那麼這些接口之間有什麼區別呢?
Runable
做爲咱們多線程開發中常用到的接口,它定義run方法,只要對象實現這個方法,將對象做爲參數輸入到new Thread(Runnable A ),線程一旦start(),那麼就 自動執行了,沒有任何的返回結果,沒法知道何時結束,適用於徹底異步的任務,不用關心結果。樣例:
Callable
Callable定義的接口call(),它可以拋出異常,而且可以有一個返回結果。實現了Callable要想提交到線程池中, 直接經過executorService.submit(new CallAbleTask(i)),可是返回的結果是Future,結果信息從Future裏面取出,具體的業務邏輯在call中執行。好了下面介紹下Future
Future
Future提供了五個接口,功能以下圖:
總的來講Future,可以控制Callable對象的執行,檢測是否作完,能夠阻塞式獲取結果,也能夠等待一段時間內獲取結果,具體的方法含義由上圖可見:
boolean cancel(boolean mayInterruptIfRunning):用來取消任務,成功返回true,失敗則返回false
boolean isCancelled():表示任務是否被取消成功,若是在任務正常完成前被取消成功,則返回 true
boolean isDone():表示任務是否已經完成,若任務完成,則返回true
V get():用來獲取執行結果,這個方法會產生阻塞會一直等到任務執行完畢才返回
V get(long timeout, TimeUnit unit) 用來獲取執行結果,若是在指定時間內,還沒獲取到結果,直接返回null
。咱們看下例子
咱們可以看到方法的執行都是Callable,可是最後獲取結果經過Future,get的方式的話,就是一直會阻塞在那裏獲取。
以上總結:
Runable適用於徹底異步的任務,不用操心執行狀況,異常出錯的。
Callable適用於須要由返回結果的,對執行中的異常要知曉的,須要提交到線程池中。
Future主要是線程池執行Callable任務,返回的結果。它可以中斷任務的執行,一直等待結果,或者等待一段時間獲取結果。
參考:Runable Callable Future 的區別與聯繫
參考:Java中繼承thread類與實現Runnable接口的區別