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中添加新的任務。