ThreadPoolExecutor優(yōu)先級隊列PriorityBlockingQueue

前兩天重構(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í)行ThreadPoolExecutorexecute方法巴帮。但其實這里的任務(wù)已經(jīng)不是最初指定的任務(wù)溯泣,而是一個Future,所以在最后嘗試將任務(wù)放進優(yōu)先級隊列時榕茧,調(diào)用比較器的compare方法時自然會拋出
java.lang.ClassCastException: java.util.concurrent.FutureTask cannot be cast to

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末垃沦,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子用押,更是在濱河造成了極大的恐慌肢簿,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜻拨,死亡現(xiàn)場離奇詭異池充,居然都是意外死亡,警方通過查閱死者的電腦和手機缎讼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門收夸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人血崭,你說我怎么就攤上這事卧惜。” “怎么了夹纫?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵咽瓷,是天一觀的道長。 經(jīng)常有香客問我舰讹,道長茅姜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任月匣,我火速辦了婚禮匈睁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘桶错。我一直安慰自己,他們只是感情好胀蛮,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布院刁。 她就那樣靜靜地躺著,像睡著了一般粪狼。 火紅的嫁衣襯著肌膚如雪退腥。 梳的紋絲不亂的頭發(fā)上任岸,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音狡刘,去河邊找鬼享潜。 笑死,一個胖子當(dāng)著我的面吹牛嗅蔬,可吹牛的內(nèi)容都是我干的剑按。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼澜术,長吁一口氣:“原來是場噩夢啊……” “哼艺蝴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鸟废,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤猜敢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后盒延,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缩擂,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年添寺,在試婚紗的時候發(fā)現(xiàn)自己被綠了胯盯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡畦贸,死狀恐怖陨闹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情薄坏,我是刑警寧澤趋厉,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站胶坠,受9級特大地震影響君账,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜沈善,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一乡数、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧闻牡,春花似錦净赴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春金度,著一層夾襖步出監(jiān)牢的瞬間应媚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工猜极, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留中姜,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓跟伏,卻偏偏與公主長得像丢胚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子酬姆,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355