在之前的Java線程池實踐當中,我遇到了任務優先級的問題。最終採取的方案是新增一個線程池作爲執行高優任務,然後將普通線程池的在執行任務執行,先去判斷高優線程池是否有等待任務,如果有就先執行高優線程池等待隊列中的任務。
雖然後期給普通異步線程池增加了雙向鏈表,直接採取插隊模式執行,但是也讓異步任務更加複雜了。所以經過一些 java.util.concurrent
包的重新學習,得到了最優的答案,就是 java.util.concurrent.PriorityBlockingQueue
。
PriorityBlockingQueue簡介
java.util.concurrent.PriorityBlockingQueue
是 Java 併發包中的一個線程安全的優先級阻塞隊列。它是基於優先級的元素順序,具有以下特點:
- 線程安全:
PriorityBlockingQueue
是線程安全的,可以在多線程環境下安全地進行操作,而不需要額外的同步手段。 - 無界隊列:
PriorityBlockingQueue
是一個無界隊列,可以無限制地添加元素,因此不會因爲隊列滿而阻塞生產者線程。 - 基於優先級的元素順序:
PriorityBlockingQueue
中的元素按照優先級順序進行排序,具有較高優先級的元素會被優先出隊。默認情況下,元素需要實現Comparable
接口來定義它們的優先級,也可以在構造函數中提供一個Comparator
來自定義優先級順序。 - 阻塞操作: 當隊列爲空時,從
PriorityBlockingQueue
中獲取元素的操作會阻塞線程,直到隊列中有元素可用;當隊列滿時,向PriorityBlockingQueue
中添加元素的操作也會阻塞線程,直到隊列有足夠的空間。 - 不支持空元素:
PriorityBlockingQueue
不支持添加空元素,即元素不能爲 null。
PriorityBlockingQueue
可以用於實現基於優先級的任務調度、事件處理等場景,其中優先級高的任務或事件會優先被處理。它提供了一種高效的方式來管理和處理具有不同優先級的元素。
多優先級線程池
下面是我自己的實現邏輯:
- 首先創建一個功能類,實現
java.lang.Comparable
和java.lang.Runnable
。 - 制定優先級規則,通常定義一個屬性
int
類型,代表優點等級。 - 封裝execute()方法,用來向線程池提交任務。
具體代碼如下:
/**
* 多優先級線程池
*/
static ThreadPoolExecutor levelPool = createPool(POOL_SIZE, POOL_SIZE, ALIVE_TIME, new PriorityBlockingQueue<PriorityTask>(), getFactory("L"), new ThreadPoolExecutor.DiscardPolicy())
/**
* 執行優先級任務
* @param task
* @return
*/
static def executeLevel(PriorityTask task) {
levelPool.execute(task)
}
/**
* 執行優先級任務,設定優先級
* @param priority
* @param closure
* @return
*/
static def executeLevel(int priority, Closure closure) {
levelPool.execute(new PriorityTask(priority) {
@Override
void run() {
closure()
}
})
}
/**
* 執行優先級任務,使用默認優先級
* @param closure
* @return
*/
static def executeLevel(Closure closure) {
levelPool.execute(new PriorityTask(PRIORITY_LEVEL_DEFAULT) {
@Override
void run() {
closure()
}
})
}
/**
* 優先級任務,用於優先級線程池
*/
static abstract class PriorityTask implements Runnable, Comparable<PriorityTask> {
int priority
PriorityTask(int priority) {
this.priority = priority
}
/**
* 比較方法,用於優先級隊列,優先級越高,越先執行
* @param o
* @return
*/
@Override
int compareTo(PriorityTask o) {
return this.priority - o.priority
}
}
測試
我們來寫一個測試用例,往線程池提交優先級不斷提升的任務,打印任務執行時間。
ThreadPoolUtil.getLevelPool().setCorePoolSize(1);
ThreadPoolUtil.getLevelPool().setMaximumPoolSize(1);
for (int i = 0; i < 10; i++) {
ThreadPoolUtil.executeLevel(new ThreadPoolUtil.PriorityTask(10 - i) {
@Override
public void run() {
SourceCode.sleep(0.01);
System.out.println(this.getPriority());
}
});
}
控制檯打印:
10
1
2
3
4
5
6
7
8
9
看起來是比較符合預期的。