新建一個線程並啟動,開銷會很大,因為運行線程需要的資源比調用對象方法需要的資源多得多。在很多情況下,線程被用於執行一類任務,而這類任務數量很多,發生的時間分布不均,如果為每個新任務都啟用一個新線程來執行,則開銷會太大,可以采用一種性能優化技術,就是使用線程池。
將若干執行任務的線程放在池中,當有任務要執行時,從池中取出一個空閒線程來處理任務,處理完任務後,再講線程對象放入池中。線程池實際上就是一個對象池,只是池中的對象都是線程。
本文實例將實現一個線程池,可以給線程池分配任務,線程池中的線程自動獲取任務並執行。
關鍵技術:1.線程組ThreadGroup可以管理多個線程,所以讓線程池繼承ThreadGroup。
2.無條件關閉線程池時,通過ThreadGroup的interrupt方法中斷池中的所有線程。
3.有條件關閉線程池時,通過ThreadGroup獲得池中所有活動線程的引用,依次調用Thread的join方法等待活動線程執行完畢。當所有線程都運行結束時,線程池才 能被關閉。
4.將任務放在LinkedList中,由於LinkedList不支持同步,所以在添加任務和獲取任務的方法聲明中必須使用Synchronized關鍵字。
實例
package book.thread.pool;
/**
*定義任務的接口類
*/
public interface Task {
public void perform() throws Exception;
}
package book.thread.pool;
public class MyTask implements Task{
private int taskID = 0;//任務ID
public MyTask(int id){
this.taskID = id;
}
@Override
public void perform() throws Exception {
System.out.println("MyTask " + taskID + ":start");
Thread.sleep(1000);
System.out.println("MyTask " + taskID + ":end");
}
}
package book.thread.pool;
import java.util.LinkedList;
public class MyThreadPool extends ThreadGroup{
private boolean isAlive;//標志線程池是否開啟
private LinkedList taskQueue;//線程池中的任務隊列
private int threadID;//線程池中的線程ID
private static int threadPoolID;//線程池ID
//創建新的線程池,numThreads是池中的線程數
public MyThreadPool(int numThreads){
super("ThreadPool-"+(threadPoolID++));
//設置該線程池的Daemon屬性為true,表示當該線程池中的所有線程都被銷毀時,該線程池會自動被銷毀
super.setDaemon(true);
this.isAlive = true;
this.taskQueue = new LinkedList();//新建一個任務隊列
//啟動numThreads個工作線程
for(int i = 0;i < numThreads; i++){
new PooledThread().start();
}
}
//添加新任務
public synchronized void performTask(Task task){
if(!this.isAlive){
throw new IllegalStateException();//線程池被關閉,則拋出異常
}
if(task != null){
this.taskQueue.add(task);//將任務放到任務隊列的尾部
notify();//通知工作線程取任務
}
}
//獲取任務
protected synchronized Task getTask() throws InterruptedException{
//如果任務列表為空,而且線程池沒有被關閉,則繼續等待任務
while(this.taskQueue.size() == 0){
if(!this.isAlive){
return null;
}
wait();
}
//取任務列表的第一個任務
return (Task)this.taskQueue.removeFirst();
}
//關閉線程池,所有線程停止,不再執行任務
public synchronized void close(){
if(isAlive){
this.isAlive = false;
this.taskQueue.clear();//清除任務
this.interrupt();//中止線程池中的所有線程
}
}
//關閉線程池,並等待線程池中的所有任務運行完成,但不能接收新任務
public void join(){
//通知其他等待線程“該線程池已關閉”的消息
synchronized(this){
isAlive = false;
notifyAll();
}
//等待所有線程完成,首先建立一個新的線程組,activeCount方法獲取線程池中活動線程的估計數
Thread[] threads = new Thread[this.activeCount()];
//將線程池中的活動線程拷貝到新創建的線程組threads中
int count = this.enumerate(threads);
for(int i = 0;i < count; i++){
try {
threads[i].join();//等待線程運行結束
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//內部類,用於執行任務的工作線程
private class PooledThread extends Thread{
public PooledThread(){
//第一個參數為該線程所在的線程組對象,即當前線程池對象
//第二個參數為線程名字
super(MyThreadPool.this,"PooledThread-" +(threadID++));
}
public void run(){
//如果該線程沒有被中止
while(!isInterrupted()){
//獲取任務
Task task = null;
try {
task = getTask();
} catch (InterruptedException e) {
e.printStackTrace();
}
//只要線程池的任務列表不為空,getTask方法就總能得到一個任務
//若getTask()返回null,則表示線程池中已經沒有任務,而且線程池已經被關閉
if(task == null){
return;
}
//運行任務,捕捉異常
try {
task.perform();
} catch (Exception e) {
uncaughtException(this,e);
}
}
}
}
}
package book.thread.pool;
public class PoolTest {
public static void main(String[] args) {
int numThreads = 3;//線程池中的線程數
MyThreadPool threadPool = new MyThreadPool(numThreads);//生成線程池
int numTasks = 10;//任務數
//運行任務
for(int i = 0;i<numTasks;i++){
threadPool.performTask(new MyTask(i));
}
//關閉線程池並等待所有任務完成
threadPool.join();
}
}
輸出結果:
MyTask 0:start
MyTask 1:start
MyTask 2:start
MyTask 0:end
MyTask 3:start
MyTask 1:end
MyTask 4:start
MyTask 2:end
MyTask 5:start
MyTask 3:end
MyTask 6:start
MyTask 4:end
MyTask 7:start
MyTask 5:end
MyTask 8:start
MyTask 6:end
MyTask 9:start
MyTask 7:end
MyTask 8:end
MyTask 9:end
結果分析:MyThreadPool類是線程池的主體類,用於管理一組工作線程。
1.繼承ThreadGroup,可以使用ThreadGroup提供的方法管理線程池中的線程。
2.performTask公有同步方法往線程池的任務隊列中添加一個任務。如果線程池已被關閉,即isAlive屬性為false,則不允許添加任務;添加任務後,調用notify方 法,通知池中的工作線程取任務。
3.getTask受保護同步方法從線程池的任務隊列中獲取一個任務。之所以聲明為受保護的,是為了限制其他類的對象非法獲取任務。如果任務隊列中沒有任務,則當 前線程進入等待狀態,如果線程池已被關閉,則直接返回null。
4.close方法強制關閉線程池。通過ThreadGroup的interrupt方法中斷線程池中所有運行的線程,清空任務隊列,並且isAlive屬性設置為false,表示不接收新任務
5.join方法有條件的關閉線程池。isAlive屬性置為false,表示線程池不再接收新任務,通過ThreadGroup獲得正在運行的線程,通過Thread的join方法等待他們執 行完任務後,再關閉線程池。
PooledThread類是MyThreadPool的內部類,定義了工作線程,處於MyThreadPool線程池中。在run放在中不斷的從線程池的任務隊列中取任務,取到任務後,調用任務的perform方法執行任務。