什麼是線程池。
線程池,其實就是一個容納多個線程的容器,其中的線程可以反覆使用,省去了頻繁創建線程對象的操作,無需反覆創建線程而消耗過多資源。
爲什麼要使用線程池。
在java中,如果每個請求到達就創建一個新線程,開銷是相當大的。在實際使用中,創建和銷燬線程花費的時間和消耗的系統資源都相當大,甚至可能要比在處理實際的用戶請求的時間和資源要多的多。除了創建和銷燬線程的開銷之外,活動的線程也需要消耗系統資源。如果在一個jvm裏創建太多的線程,可能會使系統由於過度消耗內存或“切換過度”而導致系統資源不足。爲了防止資源不足,需要採取一些辦法來限制任何給定時刻處理的請求數目,儘可能減少創建和銷燬線程的次數,特別是一些資源耗費比較大的線程的創建和銷燬,儘量利用已有對象來進行服務。
線程池主要用來解決線程生命週期開銷問題和資源不足問題。通過對多個任務重複使用線程,線程創建的開銷就被分攤到了多個任務上了,而且由於在請求到達時線程已經存在,所以消除了線程創建所帶來的延遲。這樣,就可以立即爲請求服務,使用應用程序響應更快。另外,通過適當的調整線程中的線程數目可以防止出現資源不足的情況。
使用線程池方式--Runnable接口
通常,線程池都是通過線程池工廠創建,再調用線程池中的方法獲取線程,再通過線程去執行任務方法。
Executors:線程池創建工廠類
public static ExecutorService newFixedThreadPool(int nThreads):返回線程池對象
ExecutorService:線程池類
Future<?> submit(Runnable task):獲取線程池中的某一個線程對象,並執行
Future接口:用來記錄線程任務執行完畢後產生的結果。線程池創建與使用
使用線程池中線程對象的步驟:
創建線程池對象
創建Runnable接口子類對象
提交Runnable接口子類對象
關閉線程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Chi {
public static void main(String[] args) {
/*沒有線程池的寫法
Runnable r = new MyRunnable();
Thread t = new Thread(r);
t.start();*/
ExecutorService e =Executors.newFixedThreadPool(2);//創建一個包含兩個線程的線程池
Runnable r = new MyRunnable();
e.submit(r);//獲取線程池中的某一個線程對象,然後調用runnable接口中的run方法
e.submit(r);
e.submit(r);
e.submit(r);//注意run方法運行完,線程中的線程並不消耗,而是歸還到池中
e.shutdown();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("給我一個線程:"+Thread.currentThread().getName());
try {
System.out.println("線程開始消耗資源"+Thread.currentThread().getName());
Thread.sleep(2000);
System.out.println("線程使用完畢"+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("歸還到線程池中"+Thread.currentThread().getName());
}
}
運行結果:
給我一個線程:pool-1-thread-1
給我一個線程:pool-1-thread-2
線程開始消耗資源pool-1-thread-1
線程開始消耗資源pool-1-thread-2
線程使用完畢pool-1-thread-2
歸還到線程池中pool-1-thread-2
給我一個線程:pool-1-thread-2
線程開始消耗資源pool-1-thread-2
線程使用完畢pool-1-thread-1
歸還到線程池中pool-1-thread-1
給我一個線程:pool-1-thread-1
線程開始消耗資源pool-1-thread-1
線程使用完畢pool-1-thread-1
歸還到線程池中pool-1-thread-1
線程使用完畢pool-1-thread-2
歸還到線程池中pool-1-thread-2
使用線程池方式—Callable接口
Callable接口:與Runnable接口功能相似,用來指定線程的任務。其中的call()方法,用來返回線程任務執行完畢後的結果,call方法可拋出異常。
ExecutorService:線程池類
<T> Future<T> submit(Callable<T> task):獲取線程池中的某一個線程對象,並執行線程中的call()方法
V get() 獲取Future對象中封裝的數據結果
Future接口:用來記錄線程任務執行完畢後產生的結果。線程池創建與使用
使用線程池中線程對象的步驟:
創建線程池對象
創建Callable接口子類對象
提交Callable接口子類對象
關閉線程池
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class chi1 {
public static void main(String[] args) {
try{
//MyCallable my=new MyCallable(1,1);
// FutureTask<Integer> ft = new FutureTask<Integer>(my);
//new Thread(ft,"有返回值的線程").start();
// Integer integer = ft.get();
// System.out.println("子線程的返回值:"+integer);
//創建線程池對象
ExecutorService threadPool = Executors.newFixedThreadPool(2);
//創建一個Callable接口子類對象
MyCallable c = new MyCallable(100, 200);
MyCallable c2 = new MyCallable(10, 20);
//獲取線程池中的線程,調用Callable接口子類對象中的call()方法, 完成求和操作
Future<Integer> result = threadPool.submit(c);
//此 Future 的 get 方法所返回的結果類型
Integer sum= result.get();
System.out.println("sum=" + sum);
Future<Integer> result1 = threadPool.submit(c2);
Integer sum1 = result1.get();
System.out.println("sum1=" + sum1);
Future<Integer> result2 = threadPool.submit(c2);
Integer sum2 = result2.get();
System.out.println("sum2=" + sum2);
Future<Integer> result3 = threadPool.submit(c2);
Integer sum3 = result3.get();
System.out.println("sum3=" + sum3);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class MyCallable implements Callable<Integer>{
//成員變量
int x = 5;
int y = 3;
//構造方法
public MyCallable(int x, int y){
this.x = x;
this.y = y;
}
@Override
public Integer call() throws Exception {
System.out.println("給我一個線程"+Thread.currentThread().getName());
System.out.println("線程開始消耗資源"+Thread.currentThread().getName());
Thread.sleep(2000);
System.out.println("線程使用完畢"+Thread.currentThread().getName());
System.out.println("歸還到線程池中"+Thread.currentThread().getName());
return x+y;
}
}