java自定義線程池--ThreadPoolExecutors

ThreadPoolExecutor類簡介

在java線程池中的newCachedThreadPool,newFixedThreadPool,newSingleThreadExecutor,newScheduledThreadPool這四個線程池在底層都是調用了ThreadPoolExecutor()這個構造方法。

若Executors這個類無法滿足我們的需求的時候,可以自己創建自定義的線程池,ThreadPoolExecutor類的定義如下

	public ThreadPoolExecutor(int corePoolSize,//核心線程數--線程池初始化創建的線程數量
            int maximumPoolSize,//最大線程數,線程池中能創建的最大線程數
            long keepAliveTime,//線程存活時間
            TimeUnit unit,//線程存貨時間單位
            BlockingQueue<Runnable> workQueue,//一個阻塞隊列
            ThreadFactory threadFactory//拒絕策略
            ) {……}

那麼在使用這個方法自定義線程池的時候我們要注意些什麼呢 ?

這個ThreadPoolExecutor()構造方法,對於隊列是什麼類型的比較關鍵,

1.在使用有界隊列的時候

若有新的任務需要執行,如果線程池實際線程數小於corePoolSize核心線程數的時候,則優先創建線程。若大於corePoolSize時,則會將多餘的線程存放在隊列中,若隊列已滿,且最請求線程小於maximumPoolSize的情況下,則自定義的線程池會創建新的線程,若隊列已滿,且最請求線程大於maximumPoolSize的情況下,則執行拒絕策略,或其他自定義方式。

一個小的Demo說明上面的情況
public class MyTask implements Runnable{
	
private int id;
private String name;
public int getId() {
	return id;
}
public void setId(int id) {
	this.id = id;
}
public String getName() {
	return name;
}
public void setName(String name) {
	this.name = name;
}
public MyTask(int id, String name) {
	super();
	this.id = id;
	this.name = name;
}
@Override
public void run() {
	try {
		System.out.println("run taskId = "+this.id);
		Thread.sleep(5000);
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}
@Override
	public String toString() {
		return Integer.toString(this.id);
	}
}
上面創建了一個類實現了Runable()接口
public class MyThreadPool {

	public static void main(String[] args) {
		/**
		 * 1.在使用有界隊列的時候:若有新的任務需要執行,如果線程池實際線程數小於corePoolSize核心線程數的時候,則優先創建線程。
		 * 若大於corePoolSize時,則會將多餘的線程存放在隊列中,
		 * 若隊列已滿,且最請求線程小於maximumPoolSize的情況下,則自定義的線程池會創建新的線程,
		 * 若隊列已滿,且最請求線程大於maximumPoolSize的情況下,則執行拒絕策略,或其他自定義方式。
		 */
		ThreadPoolExecutor pool = new ThreadPoolExecutor(// 自定義一個線程池
				1, // coreSize
				2, // maxSize
				60, // 60s
				TimeUnit.SECONDS, new ArrayBlockingQueue<>(3) // 有界隊列,容量是3個
		);

		MyTask task1 = new MyTask(1, "task1");
		MyTask task2 = new MyTask(2, "task2");
		MyTask task3 = new MyTask(3, "task3");
		MyTask task4 = new MyTask(4, "task4");
		MyTask task5 = new MyTask(5, "task5");
		MyTask task6 = new MyTask(6, "task6");
		/**
		 * 此處可以一步步打開看執行結果是不是符合上面註釋所說的情況。
		 */
		pool.execute(task1);
		pool.execute(task2);
		pool.execute(task3);
		pool.execute(task4);
		// pool.execute(task5);
		// pool.execute(task6);

		pool.shutdown();
	}

}
創建了一個自定義的線程池。
此處我們可以把任務一個一個加入到線程池中去驗證上述情況看是否正確。
1.放入task1的時候馬上執行,然後休眠5s關閉線程池。
2.放入task1和task2的時候我們發現,此時task1先執行,然後task2等待5s執行,然後等待5s線程池調用shutdown()方法關閉。
3.放入3個任務和4個的時候發現都是一次睡眠等待執行,關閉。
4.放入5個的時候我們發現線程1和線程5是同時執行的。然後依次執行task2,3,4.此時我們coreSize是1隊列是滿的,此時會創建一個線程。因爲是5個任務,隊列是滿的,並且這種情況maxSize還可以創建一個線程執行,所有1和5是同時打印的,然後按照順序依次執行。
5.放入6個任務的時候我們發現1和5執行完之後task6會拋出個異常不去執行,然後執行2.3.4.

由此可以驗證我們上面說的結論。

2.在使用無界隊列的時候:

LinkedBlockingQueue與有界隊列相比,除非系統資源耗盡,否則無界隊列不存在任務入隊失敗的情況,若系統的線程數小於corePoolSize時,則新建線程執行corePoolSize,當達到corePoolSize後,則把多餘的任務放入隊列中等待執行若任務的創建和處理的速速差異很大,無界隊列會保持快速增長,直到耗盡系統內存爲之,對於無界隊列的線程池maximumPoolSize並無真實用處。

無界隊列這個很好理解,此處就不做代碼說明。

3.拒絕策略

jdk給我們提供了一些拒絕策略

1.AbortPolicy:直接拋出異常,系統正常工作。(默認的策略)
2.CallerRunsPolicy:只要線程池未關閉,該策略直接在調用者線程中執行,運行當前被丟棄的任務。
3.DiscardOrderstPolicy:丟棄最老的請求,嘗試再次提交當前任務。
4.丟棄無法處理的任務,不給於任何處理。
如果需要自定義策略,需要實現RejectedExecutionHandler接口。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章