多線程(五) 線程池

線程池方式

線程池的思想

咱們使用線程的時候就去建立一個線程,這樣實現起來很是簡便,可是就會有一個問題: 若是併發的線程數量不少,而且每一個線程都是執行一個時間很短的任務就結束了,這樣頻繁建立線程就會大大下降 系統的效率,由於頻繁建立線程和銷燬線程須要時間。那麼有沒有一種辦法使得線程能夠複用,就是執行完一個任務,並不被銷燬,而是能夠繼續執行其餘的任務? 在Java中能夠經過線程池來達到這樣的效果。java

線程池概念

線程池:其實就是一個容納多個線程的容器,其中的線程能夠反覆使用,省去了頻繁建立線程對象的操做, 無需反覆建立線程而消耗過多資源。服務器

因爲線程池中有不少操做都是與優化資源相關的,咱們在這裏就很少贅述。咱們經過一張圖來了解線程池的工做原 理:併發

合理利用線程池可以帶來三個好處:dom

1. 下降資源消耗。減小了建立和銷燬線程的次數,每一個工做線程均可以被重複利用,可執行多個任務。ide

2. 提升響應速度。當任務到達時,任務能夠不須要的等到線程建立就能當即執行。工具

3. 提升線程的可管理性。能夠根據系統的承受能力,調整線程池中工做線線程的數目,防止由於消耗過多的內 存,而把服務器累趴下(每一個線程須要大約1MB內存,線程開的越多,消耗的內存也就越大,最後死機)。測試

線程池的使用

Java裏面線程池的頂級接口是 java.util.concurrent.Executor ,可是嚴格意義上講 Executor 並非一個線 程池,而只是一個執行線程的工具。真正的線程池接口是 java.util.concurrent.ExecutorService 。 要配置一個線程池是比較複雜的,尤爲是對於線程池的原理不是很清楚的狀況下,頗有可能配置的線程池不是較優 的,所以在 java.util.concurrent.Executors 線程工廠類裏面提供了一些靜態工廠,生成一些經常使用的線程池。 官方建議使用Executors工程類來建立線程池對象。 Executors類中有個建立線程池的方法以下:優化

public static ExecutorService newFixedThreadPool(int nThreads) :返回線程池對象。(建立的是 有界線程池,也就是池中的線程個數能夠指定最大數量)線程

獲取到了一個線程池ExecutorService 對象,那麼怎麼使用呢,在這裏定義了一個使用線程池對象的方法以下: public Future submit(Runnable task) :獲取線程池中的某一個線程對象,並執行code

Future接口:用來記錄線程任務執行完畢後產生的結果。

使用線程池中線程對象的步驟:

1. 建立線程池對象。

2. 建立Runnable接口子類對象。(task)

3. 提交Runnable接口子類對象。(take task)

4. 關閉線程池(通常不作)。

Runnable實現類代碼:

public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("我要一個教練");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("教練來了: " + Thread.currentThread().getName());
System.out.println("教我游泳,交完後,教練回到了游泳池");
}
}

線程池測試類:

public class Demo01ThreadPool {
public static void main(String[] args) {
// 建立線程池對象
ExecutorService service = Executors.newFixedThreadPool(2);//包含2個線程對象
// 建立Runnable實例對象
MyRunnable r = new MyRunnable();
//本身建立線程對象的方式
// Thread t = new Thread(r);
// t.start(); ---> 調用MyRunnable中的run()
// 從線程池中獲取線程對象,而後調用MyRunnable中的run()
service.submit(r);
// 再獲取個線程對象,調用MyRunnable中的run()
service.submit(r);
service.submit(r);
// 注意:submit方法調用結束後,程序並不終止,是由於線程池控制了線程的關閉。
// 將使用完的線程又歸還到了線程池中
// 關閉線程池
//service.shutdown();
}
}

Callable測試代碼:

Future submit(Callable task) : 獲取線程池中的某一個線程對象,並執行.

Future : 表示計算的結果.

V get() : 獲取計算完成的結果。

public class Demo02ThreadPool {
public static void main(String[] args) throws Exception {
// 建立線程池對象
ExecutorService service = Executors.newFixedThreadPool(2);//包含2個線程對象
// 建立Runnable實例對象
Callable<Double> c = new Callable<Double>() {
@Override
public Double call() throws Exception {
return Math.random();
}
};
// 從線程池中獲取線程對象,而後調用Callable中的call()
Future<Double> f1 = service.submit(c);
// Futur 調用get() 獲取運算結果
System.out.println(f1.get());
Future<Double> f2 = service.submit(c);
System.out.println(f2.get());
Future<Double> f3 = service.submit(c);
System.out.println(f3.get());
}
}