Java多線程(四) 線程池,java多線程線程池
一個優秀的軟件不會隨意的創建很銷毀線程,因為創建和銷毀線程需要耗費大量的CPU時間以及需要和內存做出大量的交互。因此JDK5提出了使用線程池,讓程序員把更多的精力放在業務邏輯上面,弱化對線程的開閉管理。
JDK提供了四種不同的線程池給程序員使用
首先使用線程池,需要用到ExecutorService接口,該接口有個抽象類AbstractExecutorService對其進行了實現,ThreadPoolExecutor進一步對抽象類進行了實現。最後JDK封裝了一個Executor類對ThreadPoolExecutor進行實例化,因此通過Executor能夠創建出具有如下四種特性的線程池
1. 無下界線程池
ExecutorService threadPool= Executors.newCachedThreadPool( ); 當線程數不足時,線程池會動態增加線程進行後續的任務調度
2. 固定線程數的線程池
ExecutorService threadPool= Executors.newFixedThreadPool(3); 創建具有固定線程數:3個的線程池
3. 單線程線程池
ExecutorService threadPool= Executors.newSingleThreadScheduledExecutor( );創建單例線程池,確保線程池內恆定有1條線程在工作
4. 調度線程池
ScheduledExecutorService threadPool= Executors.newSingleThreadScheduledExecutor( ); 創建一個定長線程池,定時或周期性地執行任務

![]()
1 package com.scl.thread.threadPool;
2
3 import java.util.concurrent.Executors;
4 import java.util.concurrent.ScheduledExecutorService;
5 import java.util.concurrent.TimeUnit;
6
7 public class ScheduledThreadPool
8 {
9 public static void main(String[] args)
10 {
11 // 創建定時調度線程池
12 ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();
13 // 定時執行Runnable
14 threadPool.scheduleAtFixedRate(new Runnable()
15 {
16
17 @Override
18 public void run()
19 {
20 System.out.println("do some big Scheduled");
21 }
22 }, 10, 3, TimeUnit.SECONDS);
23 }
24 }
定時調度線程池
使用線程池也比較簡單,只要往線程池裡面提交"任務"即可,一般使用對象.submit的方法進行,如下:
① threadPool.submit(Callable<T>task);
Callable跟Runnable接口一樣在使用接口時會自動調用裡面的call方法。與Runnable接口不一樣的地方在於run方法不能返回內容,call方法含有返回參數,在外部可以通過Future 接口來接受線程返回的結果。如:Future<String> future = threadPool.submit(new MyTask( ));
② threadPool.submit(Runnable task);
由這個簽名可以看出,同樣地可以往線程池裡面扔入Runnable接口實例。
這如上面所說,線程池的使用就是減少程序員對線程的啟動和關閉。把線程的創建及關閉交給線程池來完成。因此使用線程池來完成線程的經典問題:生產者-消費者模型。
模型簡介:該模型是一個典型的排隊內容。模型分為三個主要角色:生產者、消費者、倉庫。生產者和消費者共享倉庫信息,①當倉庫裡面的產品滿了,停止生產等待消費者去消費到足夠的量再進行生產 ②當倉庫裡面的產品為空,等待生產者生產後再進行銷售 ③倉庫負責承載產品內容
根據模型內容,做成了一個車位管理的例子。分為三個角色:持車人(CarOwner)、保安(Secure)、車庫(CarPark)。不同的持車人把車駛入到車庫,保安負責記錄車輛離開車庫的數量。車庫容量固定,如果車庫為空保安可以通知持車人把車駛入車庫。如果車庫滿了,保安告知持車人等待另外的持車人把車駕駛出來。車庫使用一個列表對入庫的車信息進行記錄。
因此有如下類圖:


![]()
1 package com.scl.thread.threadPool.CarParkManager;
2
3 public class CarOwner implements Runnable
4 {
5 private int driverInNum;
6 private CarPark carPark;
7
8 public CarOwner(CarPark carPark)
9 {
10 this.carPark = carPark;
11 }
12
13 @Override
14 public void run()
15 {
16 carPark.driverIn(driverInNum);
17 }
18
19 public int getDriverInNum()
20 {
21 return driverInNum;
22 }
23
24 public void setDriverInNum(int driverInNum)
25 {
26 this.driverInNum = driverInNum;
27 }
28 }
持車人 CarOwner
![]()
停車場 CarPark

![]()
1 package com.scl.thread.threadPool.CarParkManager;
2
3 public class Car
4 {
5 private String id;
6 private String name;
7
8 public Car(String id, String name)
9 {
10 this.id = id;
11 this.name = name;
12 }
13
14 }
汽車信息類 Car

![]()
1 package com.scl.thread.threadPool.CarParkManager;
2
3 public class Secure implements Runnable
4 {
5 private int driverOutNum;
6
7 private CarPark carPark;
8
9 public Secure(CarPark carPark)
10 {
11 this.carPark = carPark;
12 }
13
14 public int getDriverOutNum()
15 {
16 return driverOutNum;
17 }
18
19 public void setDriverOutNum(int driverOutNum)
20 {
21 this.driverOutNum = driverOutNum;
22 }
23
24 @Override
25 public void run()
26 {
27 carPark.driverOut(driverOutNum);
28 }
29 }
保安 Secute

![]()
1 package com.scl.thread.threadPool.CarParkManager;
2
3 import java.util.concurrent.ExecutorService;
4 import java.util.concurrent.Executors;
5
6 public class Client
7 {
8
9 public static void main(String[] args)
10 {
11 // 設置車庫最大值
12 CarPark carPark = new CarPark(5);
13 // 創建線程池
14 ExecutorService threadPool = Executors.newCachedThreadPool();
15 // 創建三個持車人
16 CarOwner cw1 = new CarOwner(carPark);
17 CarOwner cw2 = new CarOwner(carPark);
18 CarOwner cw3 = new CarOwner(carPark);
19 // 設置需要駛入的車輛數目
20 cw1.setDriverInNum(3);
21 cw2.setDriverInNum(3);
22 cw3.setDriverInNum(1);
23
24 // 創建三個保安
25 Secure s1 = new Secure(carPark);
26 Secure s2 = new Secure(carPark);
27 Secure s3 = new Secure(carPark);
28 s1.setDriverOutNum(1);
29 s2.setDriverOutNum(2);
30 s3.setDriverOutNum(1);
31
32 threadPool.submit(cw1);
33 threadPool.submit(cw2);
34 threadPool.submit(cw3);
35 threadPool.submit(s1);
36 threadPool.submit(s2);
37 threadPool.submit(s3);
38
39 }
40
41 }
客戶端 Client
由上述代碼可見,使用線程池和使用Thread類進行提交沒有太大的差異。JDK5提供了一個阻塞隊列的,能夠更好地模擬生產者-消費者模型。後續再進行總結。
以上為線程池的總結內容,如有錯漏煩請指出糾正。