前兩天重構(gòu)代碼枚冗,調(diào)試的時候角塑,發(fā)現(xiàn)有個使用到線程池的地方拋出java.lang.ClassCastException: java.util.concurrent.FutureTask cannot be cast to
異常
這個代碼是線上在跑的一個邏輯蝎土,不該出現(xiàn)問題才對慨代,最后還是翻了下源碼確定原因
原因:之前向線程池提交任務(wù)用的是execute
方法息罗,復(fù)制的時候錯用成了submit
方法疚鲤,改回execute
方法即可
既然遇到了锥累,順便記錄下
自定義提交到線程池的任務(wù)
@Data
@AllArgsConstructor
class TestRunnable implements Runnable {
private Integer i;
@Override
public void run() {
try {
Thread.sleep(1000 * i);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
}
-
線程池配合優(yōu)先級隊列的使用
execute
public static void main(String[] args) {
// 創(chuàng)建優(yōu)先級隊列,指定隊列初始大小 指定隊列中的任務(wù)比較器
// 優(yōu)先級隊列是無界的 指定的只是初始大小
// 可以使用lambda簡化
PriorityBlockingQueue queue = new PriorityBlockingQueue(100, new Comparator<TestRunnable>() {
@Override
public int compare(TestRunnable o1, TestRunnable o2) {
return o1.getI() - o2.getI();
}
});
// 創(chuàng)建線程池 傳入優(yōu)先級隊列
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, queue);
threadPoolExecutor.execute(new TestRunnable(5));
threadPoolExecutor.execute(new TestRunnable(2));
threadPoolExecutor.execute(new TestRunnable(1));
threadPoolExecutor.execute(new TestRunnable(3));
}
執(zhí)行結(jié)果
5
1
2
3
根據(jù)執(zhí)行結(jié)果可以看到任務(wù)已經(jīng)被排過序
源碼筆記
ThreadPoolExecutor execute
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 線程池有正在執(zhí)行的任務(wù)集歇,其余任務(wù)會在指定的隊列上排隊
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
這里指定的是PriorityBlockingQueue
桶略,可以看下該隊列增加任務(wù)的方法
public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
final ReentrantLock lock = this.lock;
lock.lock();
int n, cap;
Object[] array;
while ((n = size) >= (cap = (array = queue).length))
tryGrow(array, cap);
try {
Comparator<? super E> cmp = comparator;
// cmp是創(chuàng)建隊列時指定的比較器
if (cmp == null)
siftUpComparable(n, e, array);
else
siftUpUsingComparator(n, e, array, cmp);
size = n + 1;
notEmpty.signal();
} finally {
lock.unlock();
}
return true;
}
siftUpUsingComparator
方法
private static <T> void siftUpUsingComparator(int k, T x, Object[] array,
Comparator<? super T> cmp) {
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = array[parent];
// 可以看到調(diào)用了比較器的compare方法,以確定任務(wù)的優(yōu)先級
// x e是指定的任務(wù)
if (cmp.compare(x, (T) e) >= 0)
break;
array[k] = e;
k = parent;
}
array[k] = x;
}
-
線程池配合優(yōu)先級隊列的使用
submit
public static void main(String[] args) {
// 創(chuàng)建優(yōu)先級隊列,指定隊列初始大小 指定隊列中的任務(wù)比較器
// 優(yōu)先級隊列是無界的 指定的只是初始大小
// 可以使用lambda簡化
PriorityBlockingQueue queue = new PriorityBlockingQueue(4, new Comparator<TestRunnable>() {
@Override
public int compare(TestRunnable o1, TestRunnable o2) {
return o1.getI() - o2.getI();
}
});
// 創(chuàng)建線程池 傳入優(yōu)先級隊列
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, queue);
threadPoolExecutor.submit(new TestRunnable(5));
threadPoolExecutor.submit(new TestRunnable(2));
threadPoolExecutor.submit(new TestRunnable(1));
threadPoolExecutor.submit(new TestRunnable(3));
}
執(zhí)行結(jié)果
Exception in thread "main" java.lang.ClassCastException: java.util.concurrent.FutureTask cannot be cast to com.tianwen.jdk.TestRunnable
at com.tianwen.jdk.DemoApplication$1.compare(DemoApplication.java:17)
at java.util.concurrent.PriorityBlockingQueue.siftUpUsingComparator(PriorityBlockingQueue.java:375)
at java.util.concurrent.PriorityBlockingQueue.offer(PriorityBlockingQueue.java:492)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1371)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
at com.tianwen.jdk.DemoApplication.main(DemoApplication.java:31)
5
2
可以看到际歼,除了一個正在被執(zhí)行的任務(wù)和一個排在隊列頭部的任務(wù)惶翻,其余的任務(wù)添加時,在調(diào)用compare
方法都會拋出異常
源碼筆記
ThreadPoolExecutor submit
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
// 封裝成一個Future
RunnableFuture<Void> ftask = newTaskFor(task, null);
// 同樣的在執(zhí)行ThreadPoolExecutor的execute方法
// 但是傳入的已經(jīng)不是最初的任務(wù)蹬挺,而是一個封裝過的Future
execute(ftask);
return ftask;
}
實際是在父類AbstractExecutorService
中维贺,可以看到submit
方法內(nèi)先將提交給線程池的任務(wù)封裝成一個Future
,再同樣執(zhí)行ThreadPoolExecutor
的execute
方法巴帮。但其實這里的任務(wù)已經(jīng)不是最初指定的任務(wù)溯泣,而是一個Future
,所以在最后嘗試將任務(wù)放進優(yōu)先級隊列時榕茧,調(diào)用比較器的compare
方法時自然會拋出
java.lang.ClassCastException: java.util.concurrent.FutureTask cannot be cast to