成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Java多線程(3):取消正在運(yùn)行的任務(wù)

terro / 2338人閱讀

摘要:比如上一篇文章提到的線程池的方法,它可以在線程池中運(yùn)行一組任務(wù),當(dāng)其中任何一個(gè)任務(wù)完成時(shí),方法便會(huì)停止阻塞并返回,同時(shí)也會(huì)取消其他任務(wù)。

當(dāng)一個(gè)任務(wù)正在運(yùn)行的過(guò)程中,而我們卻發(fā)現(xiàn)這個(gè)任務(wù)已經(jīng)沒有必要繼續(xù)運(yùn)行了,那么我們便產(chǎn)生了取消任務(wù)的需要。比如 上一篇文章 提到的線程池的 invokeAny 方法,它可以在線程池中運(yùn)行一組任務(wù),當(dāng)其中任何一個(gè)任務(wù)完成時(shí),invokeAny 方法便會(huì)停止阻塞并返回,同時(shí)也會(huì) 取消其他任務(wù)。那我們?nèi)绾稳∠粋€(gè)正在運(yùn)行的任務(wù)?

前面兩篇多線程的文章都有提到 Future 接口和它的一個(gè)實(shí)現(xiàn)類 FutureTask,并且我們已經(jīng)知道 Future 可以用來(lái)和已經(jīng)提交的任務(wù)進(jìn)行交互。Future 接口定義了如下幾個(gè)方法:

get 方法:通過(guò)前面文章的介紹,我們已經(jīng)了解了 get 方法的使用 —— get 方法 用來(lái)返回和 Future 關(guān)聯(lián)的任務(wù)的結(jié)果。帶參數(shù)的 get 方法指定一個(gè)超時(shí)時(shí)間,在超時(shí)時(shí)間內(nèi)該方法會(huì)阻塞當(dāng)前線程,直到獲得結(jié)果 。

如果在給定的超時(shí)時(shí)間內(nèi)沒有獲得結(jié)果,那么便拋出 TimeoutException 異常;

或者執(zhí)行的任務(wù)被取消(此時(shí)拋出 CancellationException 異常);

或者執(zhí)行任務(wù)時(shí)出錯(cuò),即執(zhí)行過(guò)程中出現(xiàn)異常(此時(shí)拋出 ExecutionException 異常);

或者當(dāng)前線程被中斷(此時(shí)拋出 InterruptedException 異常 —— 注意,當(dāng)前線程是指調(diào)用 get 方法的線程,而不是運(yùn)行任務(wù)的線程)。

不帶參數(shù)的 get 可以理解為超時(shí)時(shí)間無(wú)限大,即一直等待直到獲得結(jié)果或者出現(xiàn)異常。

cancel(boolean mayInterruptIfRunning) 方法:該方法是非阻塞的。通過(guò) JDK 的文檔,我們可以知道 該方法便可以用來(lái)(嘗試)終止一個(gè)任務(wù)。

如果任務(wù)運(yùn)行之前調(diào)用了該方法,那么任務(wù)就不會(huì)被運(yùn)行;

如果任務(wù)已經(jīng)完成或者已經(jīng)被取消,那么該方法方法不起作用;

如果任務(wù)正在運(yùn)行,并且 cancel 傳入?yún)?shù)為 true,那么便會(huì)去終止與 Future 關(guān)聯(lián)的任務(wù)。

cancel(false)cancel(true)的區(qū)別在于,cancel(false)取消已經(jīng)提交但還沒有被運(yùn)行的任務(wù)(即任務(wù)就不會(huì)被安排運(yùn)行);而 cancel(true) 會(huì)取消所有已經(jīng)提交的任務(wù),包括 正在等待的正在運(yùn)行的 任務(wù)。

isCancelled 方法:該方法是非阻塞的。在任務(wù)結(jié)束之前,如果任務(wù)被取消了,該方法返回 true,否則返回 false;如果任務(wù)已經(jīng)完成,該方法則一直返回 false。

isDone 方法:該方法同樣是非阻塞的。如果任務(wù)已經(jīng)結(jié)束(正常結(jié)束,或者被取消,或者執(zhí)行出錯(cuò)),返回 true,否則返回 false。

然后我們來(lái)實(shí)踐下 Futurecancel 方法的功能:

import java.util.concurrent.*;

public class FutureTest {

    public static void main(String[] args) throws Exception {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();

        SimpleTask task = new SimpleTask(3_000); // task 需要運(yùn)行 3 秒
        Future future = threadPool.submit(task);
        threadPool.shutdown(); // 發(fā)送關(guān)閉線程池的指令

        double time = future.get();
        System.out.format("任務(wù)運(yùn)行時(shí)間: %.3f s
", time);

    }

    private static final class SimpleTask implements Callable {

        private final int sleepTime; // ms

        public SimpleTask(int sleepTime) {
            this.sleepTime = sleepTime;
        }

        @Override
        public Double call() throws Exception {
            double begin = System.nanoTime();

            Thread.sleep(sleepTime);

            double end = System.nanoTime();
            double time = (end - begin) / 1E9;

            return time; // 返回任務(wù)運(yùn)行的時(shí)間,以 秒 計(jì)
        }

    }
}

運(yùn)行結(jié)果(任務(wù)正常運(yùn)行):

然后我們定義一個(gè)用來(lái)取消任務(wù)的方法:

private static void cancelTask(final Future future, final int delay) {

    Runnable cancellation = new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(delay);
                future.cancel(true); // 取消與 future 關(guān)聯(lián)的正在運(yùn)行的任務(wù)
            } catch (InterruptedException ex) {
                ex.printStackTrace(System.err);
            }
        }
    };

    new Thread(cancellation).start();
}

然后修改 main 方法:

public static void main(String[] args) {
    ExecutorService threadPool = Executors.newSingleThreadExecutor();

    SimpleTask task = new SimpleTask(3_000); // task 需要運(yùn)行 3 秒
    Future future = threadPool.submit(task);
    threadPool.shutdown(); // 發(fā)送關(guān)閉線程池的指令

    cancelTask(future, 2_000); // 在 2 秒之后取消該任務(wù)

    try {
        double time = future.get();
        System.out.format("任務(wù)運(yùn)行時(shí)間: %.3f s
", time);
    } catch (CancellationException ex) {
        System.err.println("任務(wù)被取消");
    } catch (InterruptedException ex) {
        System.err.println("當(dāng)前線程被中斷");
    } catch (ExecutionException ex) {
        System.err.println("任務(wù)執(zhí)行出錯(cuò)");
    }

}

運(yùn)行結(jié)果:

可以看到,當(dāng)任務(wù)被取消時(shí),Futureget 方法拋出了 CancellationException 異常,并且成功的取消了任務(wù)(從構(gòu)建(運(yùn)行)總時(shí)間可以發(fā)現(xiàn))。

這樣就可以了嗎?調(diào)用 Futurecancel(true) 就一定能取消正在運(yùn)行的任務(wù)嗎?

我們來(lái)寫一個(gè)真正的耗時(shí)任務(wù),判斷一個(gè)數(shù)是否為素?cái)?shù),測(cè)試數(shù)據(jù)為 1000000033 (它是一個(gè)素?cái)?shù))。

import java.util.concurrent.*;

public class FutureTest {

    public static void main(String[] args) throws Exception {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();

        long num = 1000000033L;
        PrimerTask task = new PrimerTask(num);
        Future future = threadPool.submit(task);
        threadPool.shutdown();
        
        boolean result = future.get();
        System.out.format("%d 是否為素?cái)?shù)? %b
", num, result);

    }

    private static final class PrimerTask implements Callable {

        private final long num;

        public PrimerTask(long num) {
            this.num = num;
        }

        @Override
        public Boolean call() throws Exception {
            // i < num 讓任務(wù)有足夠的運(yùn)行時(shí)間
            for (long i = 2; i < num; i++) {
                if (num % i == 0) {
                    return false;
                }
            }

            return true;
        }

    }

}

在我的機(jī)器上,這個(gè)任務(wù)需要 13 秒才能運(yùn)行完畢:

然后我們修改 main 方法,在任務(wù)運(yùn)行到 2 秒的時(shí)候調(diào)用 Futurecancel(true)

public static void main(String[] args) throws Exception {
    ExecutorService threadPool = Executors.newSingleThreadExecutor();

    long num = 1000000033L;
    PrimerTask task = new PrimerTask(num);
    Future future = threadPool.submit(task);
    threadPool.shutdown(); // 發(fā)送關(guān)閉線程池的指令

    cancelTask(future, 2_000); // 在 2 秒之后取消該任務(wù)

    try {
        boolean result = future.get();
        System.out.format("%d 是否為素?cái)?shù)? %b
", num, result);
    } catch (CancellationException ex) {
        System.err.println("任務(wù)被取消");
    } catch (InterruptedException ex) {
        System.err.println("當(dāng)前線程被中斷");
    } catch (ExecutionException ex) {
        System.err.println("任務(wù)執(zhí)行出錯(cuò)");
    }
}

程序運(yùn)行到 2 秒時(shí)候的輸出:

程序的最終輸出:

可以發(fā)現(xiàn),雖然我們?nèi)∠巳蝿?wù),Futureget 方法也對(duì)我們的取消做出了響應(yīng)(即拋出 CancellationException 異常),但是任務(wù)并沒有停止,而是直到任務(wù)運(yùn)行完畢了,程序才結(jié)束。

查看 Future 的實(shí)現(xiàn)類 FutureTask 的源碼,我們來(lái)看一下調(diào)用 cancel(true) 究竟發(fā)生了什么:

原來(lái) cancel(true) 方法的原理是向正在運(yùn)行任務(wù)的線程發(fā)送中斷指令 —— 即調(diào)用運(yùn)行任務(wù)的 Threadinterrupt() 方法。

所以 如果一個(gè)任務(wù)是可取消的,那么它應(yīng)該可以對(duì) Threadinterrupt() 方法做出被取消時(shí)的響應(yīng)

ThreadisInterrupted() 方法,便可以用來(lái)判斷當(dāng)前 Thread 是否被中斷。任務(wù)開始運(yùn)行時(shí),運(yùn)行任務(wù)的線程肯定沒有被中斷,所以 isInterruped() 方法會(huì)返回 false;而 interrupt() 方法調(diào)用之后,isInterruped() 方法會(huì)返回 true
(由此我們也可以知道,Thread.sleep 方法是可以對(duì)中斷做出響應(yīng)的)

所以我們修改 PrimerTaskcall 方法,讓其可以對(duì)運(yùn)行任務(wù)的線程被中斷時(shí)做出停止運(yùn)行(跳出循環(huán))的響應(yīng):

@Override
public Boolean call() throws Exception {
    // i < num 讓任務(wù)有足夠的運(yùn)行時(shí)間
    for (long i = 2; i < num; i++) {
        if (Thread.currentThread().isInterrupted()) { // 任務(wù)被取消
            System.out.println("PrimerTask.call: 你取消我干啥?");
            return false;
        }

        if (num % i == 0) {
            return false;
        }
    }

    return true;
}

運(yùn)行結(jié)果:

可以看到程序在 2 秒的時(shí)候停止了運(yùn)行,任務(wù)被成功取消。

總結(jié):如果要通過(guò) Futurecancel 方法取消正在運(yùn)行的任務(wù),那么該任務(wù)必定是可以 對(duì)線程中斷做出響應(yīng) 的任務(wù)。通過(guò) Thread.currentThread().isInterrupted() 方法,我們可以判斷任務(wù)是否被取消,從而做出相應(yīng)的取消任務(wù)的響應(yīng)。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/66430.html

相關(guān)文章

  • (十八)java線程之Callable Future

    摘要:本人郵箱歡迎轉(zhuǎn)載轉(zhuǎn)載請(qǐng)注明網(wǎng)址代碼已經(jīng)全部托管有需要的同學(xué)自行下載引言前面我們講了那么多有關(guān)線程的知識(shí)不知道讀者有沒有想過(guò)這么一個(gè)問(wèn)題如果有這么一個(gè)比較耗時(shí)的任務(wù)必須使用線程來(lái)執(zhí)行但是在這個(gè)任務(wù)執(zhí)行完之后我需要得到這個(gè)線程的返回值以目前我們 本人郵箱: 歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明網(wǎng)址 http://blog.csdn.net/tianshi_kcogithub: https://github...

    stormgens 評(píng)論0 收藏0
  • Java線程進(jìn)階(四二)—— J.U.C之executors框架:Future模式

    摘要:本文首發(fā)于一世流云的專欄一模式簡(jiǎn)介模式是多線程設(shè)計(jì)模式中的一種常見模式,它的主要作用就是異步地執(zhí)行任務(wù),并在需要的時(shí)候獲取結(jié)果。二中的模式在多線程基礎(chǔ)之模式中,我們?cè)?jīng)給出過(guò)模式的通用類關(guān)系圖。 showImg(https://segmentfault.com/img/bVbiwcx?w=1000&h=667); 本文首發(fā)于一世流云的專欄:https://segmentfault.co...

    marek 評(píng)論0 收藏0
  • Java線程(2):使用線程池 ThreadPoolExecutor

    摘要:本文只介紹中線程池的基本使用,不會(huì)過(guò)多的涉及到線程池的原理。可緩存線程的線程池創(chuàng)建一個(gè)可緩存線程的線程池。首先是從接口繼承到的方法使用該方法即將一個(gè)任務(wù)交給線程池去執(zhí)行。方法方法的作用是向線程池發(fā)送關(guān)閉的指令。 首先,我們?yōu)槭裁葱枰€程池?讓我們先來(lái)了解下什么是 對(duì)象池 技術(shù)。某些對(duì)象(比如線程,數(shù)據(jù)庫(kù)連接等),它們創(chuàng)建的代價(jià)是非常大的 —— 相比于一般對(duì)象,它們創(chuàng)建消耗的時(shí)間和內(nèi)存都...

    darry 評(píng)論0 收藏0
  • Java線程-Callable和Future

    摘要:類提供了一些有用的方法在線程池中執(zhí)行內(nèi)的任務(wù)。在線程池提交任務(wù)后返回了一個(gè)對(duì)象,使用它可以知道任務(wù)的狀態(tài)和得到返回的執(zhí)行結(jié)果。 Callable和Future出現(xiàn)的原因 創(chuàng)建線程的2種方式,一種是直接繼承Thread,另外一種就是實(shí)現(xiàn)Runnable接口。 這2種方式都有一個(gè)缺陷就是:在執(zhí)行完任務(wù)之后無(wú)法獲取執(zhí)行結(jié)果。 如果需要獲取執(zhí)行結(jié)果,就必須通過(guò)共享變量或者使用線程通信的方式來(lái)達(dá)...

    seasonley 評(píng)論0 收藏0
  • FutureTask源碼解析(2)——深入理解FutureTask

    摘要:本文的源碼基于。人如其名,包含了和兩部分。而將一個(gè)任務(wù)的狀態(tài)設(shè)置成終止態(tài)只有三種方法我們將在下文的源碼解析中分析這三個(gè)方法。將棧中所有掛起的線程都喚醒后,下面就是執(zhí)行方法這個(gè)方法是一個(gè)空方 前言 系列文章目錄 有了上一篇對(duì)預(yù)備知識(shí)的了解之后,分析源碼就容易多了,本篇我們就直接來(lái)看看FutureTask的源碼。 本文的源碼基于JDK1.8。 Future和Task 在深入分析源碼之前,我...

    Harpsichord1207 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<