java多線程、線程池的實現
Java實現多線程的3種方法:繼承Thread類、實現runnable接口、使用ExecutorService,Callable、Future實現有返回值的多線程。前2種線程的實現方式沒有返回值,第三種實現方式可以獲取線程執行的返回值。一:繼承java.lang.Thread類
public class MyThread extends Thread {@Override public void run() { System.out.println( "my thread begin." ); try { // 休眠1000毫秒 Thread.sleep( 1000 ); } catch ( InterruptedException e ) { e.printStackTrace(); } System.out.println( "my thread over." ); } public static void main( String[] args ) { MyThread thread = new MyThread(); thread.start(); } } </pre><br />
二:實現java.lang.Runnable接口
public class MyThread implements Runnable { @Override public void run() { System.out.println( "my thread begin." ); try { // 休眠1000毫秒 Thread.sleep( 1000 ); // do something... } catch ( InterruptedException e ) { e.printStackTrace(); } System.out.println( "my thread over." ); } public static void main( String[] args ) { Thread thread = new Thread( new MyThread() ); thread.start(); } }
實現Runnable接口會比繼承Thread類更靈活一些,因為Java是單繼承,繼承了一個類就不能再繼承另外一個類,而接口可以實現多個。但是無論是繼承Thread類還是實現Runnable接口都不能獲取多線程的返回值,除非借助額外的變量,在run方法中修改一個變量,在其他地方使用這個變量,這種實現方式比較"雞肋"。三:實現java.util.concurrent.Callable接口
public class MyThread implements Callable<Object> { @Override public Object call() throws Exception { System.out.println( "my thread begin." ); try { // 休眠1000毫秒 Thread.sleep( 1000 ); } catch ( InterruptedException e ) { e.printStackTrace(); } System.out.println( "my thread over." ); return "ok"; } public static void main( String[] args ) { // 創建一個線程池 ExecutorService pool = Executors.newFixedThreadPool( 2 ); Future<Object> f = pool.submit( new MyThread() ); //關閉線程池 pool.shutdown(); try { System.out.println( f.get() ); } catch ( InterruptedException e ) { e.printStackTrace(); } catch ( ExecutionException e ) { e.printStackTrace(); }; } }
通過創建一個線程池提交一個Callable任務就可以通過Future類來獲取線程的返回值了,調用Future的get()方法的時候,如果任務沒有完成則阻塞直到任務完成。正如get()的注釋:Waits if necessary for the computation to complete, and then retrieves its result.
上面代碼中的ExecutorService接口繼承自Executor接口,Executor的實現基于生產者-消費者模式。提交任務的執行者是生產者(產生待完成的工作單元),執行任務的線程是消費者(消耗掉這些工作單元)。如果要實現一個生產者-消費者的設計,使用Executor通常是最簡單的方式。
Executors類提供了幾種創建線程池的方法,通過Exectors創建的線程池也實現了ExecutorService接口,方法如下:
1、固定大小的線程池:newFixedThreadPool(int nThreads)
創建固定大小的線程池。每次提交一個任務就創建一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,如果某個線程因為執行異常而結束,那么線程池會補充一個新線程。
2、單任務線程池:Executors.newSingleThreadExecutor()
創建一個單線程的線程池。這個線程池只有一個線程在工作,也就是相當于單線程串行執行所有任務。如果這個唯一的線程因為異常結束,那么會有一個新的線程來替代它。此線程池保證所有任務的執行順序按照任務的提交順序執行。
3、可變尺寸的線程池:Executors.newCachedThreadPool()
創建一個可緩存的線程池。如果線程池的大小超過了處理任務所需要的線程,那么就會回收部分空閑(60秒不執行任務)的線程,當任務數增加時,此線程池又可以智能的添加新線程來處理任務。此線程池不會對線程池大小做限制,線程池大小完全依賴于操作系統(或者說JVM)能夠創建的最大線程大小。
4、延遲線程池:Executors.newScheduledThreadPool(int corePoolSize)
創建一個大小無限的線程池。此線程池支持定時以及周期性執行任務的需求。
注意:shutdown()方法并不是終止線程的執行,而是禁止在這個Executor中添加新的任務。