標籤:catch this min turn comm 簡單 art 解決 java多線程
java建立多線程的方式有許多種,這裡簡要做個梳理
1. 繼承Thread類
繼承java.lang.Thread類,建立本地多線程的類,重載run()方法,調用Thread的方法啟動線程。範例程式碼如下:
MyThread.java
public class MyThread extends Thread { public void run(){ private int copy = 0; System.out.println("Thread id:" + Thread.currentThread().getName()+" == print time:" + System.currentTimeMillis()); System.out.println("Thread serialnum:" + ++copy); try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread id:" + Thread.currentThread().getName()+" == print time:" + System.currentTimeMillis()); } public static void main(String[] args){ MyThread mt1 = new MyThread(); MyThread mt2 = new MyThread(); MyThread mt3 = new MyThread(); System.out.println("Start all Threads:"); mt1.start(); mt2.start(); mt3.start(); }}
輸出結果:
Start all Threads:Thread id:Thread-0 == print time:1495975884383Thread serialnum:1Thread id:Thread-1 == print time:1495975884383Thread serialnum:1Thread id:Thread-2 == print time:1495975884383Thread serialnum:1Thread id:Thread-1 == print time:1495975885385Thread id:Thread-0 == print time:1495975885385Thread id:Thread-2 == print time:1495975885385
2. 實現Runnable介面
實現java.lang.Runnable介面的run方法,使用實現的對象執行個體化Thread類,調用Thread對象的start()方法。
範例程式碼:
public class MyRunnable implements Runnable { private int copy = 0; public void run(){ System.out.println("Thread id:" + Thread.currentThread().getName()+" == print time:" + System.currentTimeMillis()); System.out.println("Thread serialnum:" + ++copy); try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread id:" + Thread.currentThread().getName()+" == print time:" + System.currentTimeMillis()); } public static void main(String[] args){ MyRunnable mr = new MyRunnable(); System.out.println("Start all Threads:"); new Thread(mr).start(); new Thread(mr).start(); new Thread(mr).start(); }}
輸出結果:
Start all Threads:Thread id:Thread-0 == print time:1495975833588Thread id:Thread-1 == print time:1495975833588Thread serial:1Thread id:Thread-2 == print time:1495975833588Thread serial:2Thread serial:3Thread id:Thread-0 == print time:1495975834603Thread id:Thread-1 == print time:1495975834603Thread id:Thread-2 == print time:1495975834603
對比Thread方法和Runnable方法的使輸出結果不難發現:
. 二者均可實現多線程並發效果
. Runnable的方法可以使用一個Runnable對象建立多個線程,這多個線程共用其依賴的Runnable對象的資源,適用於多個線程處理相同資源的情境(網上舉得較多的例子:賣票);Thread的方法則是完全相互獨立的線程
事實上,嫌貴而言,當前Runnable使用的要比Thread方法普遍的多,除了上面說的資源共用的原因之外,Java的單繼承特性也增大了Thread方法的局限性。
3. 實現Callable介面
相對於上述繼承Thread類的方法和實現Runnable介面的方法,實現Callable介面的方法不但可以可以實現多線程並發,還能夠處理線程的傳回值或者擷取執行異常。使用Callable實現多線程編程需要實現Callable的call方法(在call方法中指定傳回值,拋出異常),構造與之關聯的FutureTask對象,啟動線程後,線程執行的結果會儲存在FutureTask對象中,可以使用FutureTask的get方法擷取。範例程式碼:
import java.util.concurrent.Callable;import java.util.concurrent.FutureTask;import static java.lang.Thread.sleep;public class MyCallable implements Callable<Integer> { private int flag = 0; public Integer call() throws Exception { System.out.println("Thread id:" + Thread.currentThread().getName() + " == print time:"+System.currentTimeMillis()); sleep(10000); System.out.println("Thread id:" + Thread.currentThread().getName() + " == print time:"+System.currentTimeMillis()); ++flag; return flag; } public static void main(String[] args) throws Exception{ MyCallable myCallable = new MyCallable(); FutureTask<Integer> future = new FutureTask<Integer>(myCallable); FutureTask<Integer> future2 = new FutureTask<Integer>(myCallable); new Thread(future).start(); new Thread(future2).start(); int Result,Result2;// while (!future.isDone()); Result = future.get();//// while(!future2.isDone()); Result2 = future2.get(); System.out.println("GET THREAD NAME:"+ Result ); System.out.println("GET THREAD NAME 2:"+Result2); }}
執行結果:
Thread id:Thread-0 == print time:1495997744617Thread id:Thread-1 == print time:1495997744617Thread id:Thread-0 == print time:1495997754625Thread id:Thread-1 == print time:1495997754625GET THREAD NAME:1GET THREAD NAME 2:2
上述代碼執行邏輯與前面Runnale的範例程式碼類似,通過構造Thread對象來啟動多線程。區別在於此處構造了futureTask方法用來用來儲存線程傳回值。需要注意的是,線程的Callable泛型介面裡的類型和call()方法的傳回值類型和FutureTask的泛型介面類型要一致。
4. 使用線程池和Executor架構
前面的三種方案,我們建立了Runnable或者Callable或者Thread的任務,扔到Thread中去啟動,我們需要手動去管理各種多線程的參數,比如線程數量,線程複用,安全執行緒的控制等。Java5以後的版本提供了Executor技術,能夠提供搭建好的線程池,為多線程提供了完整的解決方案,能夠有效管理線程數量,線程複用策略,保證安全執行緒,避免this逃逸問題等。
Executor提供的線程池方案包括newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool和SingleThreadExecutor(Fork/Join架構還提供了一個newWorkStealingPool),這些線程池分別提供了不同的線程管理方案,執行返回一個ExecutorService對象,這個對象可以調用execute()方法或者submit()方法,將前面定義的Runnable任務或者Callable任務或者Thread任務提交到各個線程去執行
我們以比較常用的newCachedThreadPool()為例,下面範例程式碼使用ExecutorService執行一個Callable對象任務,擷取每個線程的傳回值。
import java.util.ArrayList;import java.util.List;import java.util.concurrent.*;import static java.lang.Thread.sleep;public class MyCallable2 implements Callable<String> { public String call() throws Exception{ sleep(1000); System.out.println("terminate the thread : "+Thread.currentThread().getName()); return Thread.currentThread().getName(); } public static void main(String[] args){ ExecutorService es = Executors.newCachedThreadPool(); List<Future<String>> futureList = new ArrayList<Future<String>>(); for(int i=0;i<5;i++){ Future future = es.submit(new MyCallable2()); futureList.add(future); } for (Future f: futureList ) { while(!f.isDone()); try { System.out.println("EXECUTE RESULT:"+f.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } finally { es.shutdown(); } } }}
執行結果如下:
terminate the thread : pool-1-thread-5terminate the thread : pool-1-thread-4terminate the thread : pool-1-thread-3terminate the thread : pool-1-thread-2terminate the thread : pool-1-thread-1EXECUTE RESULT:pool-1-thread-1EXECUTE RESULT:pool-1-thread-2EXECUTE RESULT:pool-1-thread-3EXECUTE RESULT:pool-1-thread-4EXECUTE RESULT:pool-1-thread-5
使用Executors建立Runnable線程與建立Callable線程的方法類似,且不用管理傳回值,相對更加簡單,此處不再贅述。
java多線程建立-Thread,Runnable,callable和threadpool