一個優秀的軟件不會隨意的創建很銷毀線程,因為創建和銷毀線程需要耗費大量的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提供了一個阻塞隊列的,能夠更好地模擬生產者-消費者模型。後續再進行總結。
以上為線程池的總結內容,如有錯漏煩請指出糾正。