簡體   English   中英

future.cancel 不起作用

[英]future.cancel does not work

我有一個漂亮而緊湊的代碼,它不像我預期的那樣工作。

public class Test {

    public static void main(String[] args) {

        Runnable r = new Runnable() {
            @Override
            public void run() {
                try {
                    for (;;) {

                    }
                } finally {
                    System.out.println("FINALLY");
                }

            }
        };

        ExecutorService executor = Executors.newSingleThreadExecutor();

        Future<?> future = executor.submit(r);
        try {
            future.get(3, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            boolean c = future.cancel(true);
            System.out.println("Timeout " + c);
        } catch (InterruptedException | ExecutionException e) {
            System.out.println("interrupted");
        }
        System.out.println("END");

    }

}

輸出是:

超時真

結尾

問題:為什么不終止被調用的 Runnable 的 future.cancel(true) 方法? 程序將“END”寫入輸出后,“r”Runnable 仍在運行。

問題是你的 Runnable 是不可中斷的:任務中斷是 Java 中的一個協作過程,被取消的代碼需要定期檢查它是否被取消,否則它不會響應中斷。

您可以按如下方式修改代碼,它應該可以按預期工作:

Runnable r = new Runnable() {
    @Override public void run() {
        try {
            while (!Thread.currentThread.isInterrupted()) {}
        } finally {
            System.out.println("FINALLY");
        }
    }
};

這總是有點誤導:ExceutorService 甚至底層線程調度程序對 Runnable 正在做什么一無所知。 在您的情況下,他們不知道存在無條件循環。

所有這些方法(cancel、done、...)都與在 Executor 結構中管理 Threads 相關。 cancel從 Executor 服務的角度取消線程。

程序員必須測試 Runnable 是否被取消並且必須終止run()方法。

所以在你的情況下(如果我沒記錯的話)是這樣的:

public class Test {

public static void main(String[] args) {

    FutureTask r = new FutureTask () {
        @Override
        public void run() {
            try {
                for (;!isCancelled();) {

                }
            } finally {
                System.out.println("FINALLY");
            }

        }
    };

    ExecutorService executor = Executors.newSingleThreadExecutor();

    Future<?> future = executor.submit(r);
    try {
        future.get(3, TimeUnit.SECONDS);
    } catch (TimeoutException e) {
        boolean c = future.cancel(true);
        System.out.println("Timeout " + c);
    } catch (InterruptedException | ExecutionException e) {
        System.out.println("interrupted");
    }
    System.out.println("END");

}

}

當您取消Runnable已經開始的Future ,將在運行RunnableThread上調用interrupt方法。 但這不一定會停止線程。 事實上,如果它被困在一個緊密的循環中,就像你在這里看到的那樣, Thread不會停止。 在這種情況下, interrupt方法只是設置一個稱為“中斷狀態”的標志,它告訴線程在可以時停止。

Threadinterrupt方法Javadoc

Future.cancel()將取消任何排隊的任務,或者如果您的線程已經運行,則會在您的線程上調用Thread.interrupt()

你需要中斷你的代碼

您的代碼有責任為任何中斷做好准備。 我想說的是,每當你有一個長時間運行的任務時,你就插入一些像這樣的中斷就緒代碼:

while (... something long...) {

     ... do something long

     if (Thread.interrupted()) {
         ... stop doing what I'm doing...
     }
}

如何停止我正在做的事情?

您有多種選擇:

  1. 如果您在Runnable.run() 中,只需返回或跳出循環並完成該方法。
  2. 您可能在代碼深處使用其他方法。 此時該方法拋出InterruptedException可能是有意義的,因此您只需這樣做(清除標志)。
  3. 但也許在你的代碼深處,拋出InterruptedException是沒有意義的。 在這種情況下,您應該拋出一些其他異常,但在此之前標記您的線程再次中斷,以便捕獲的代碼知道中斷正在進行中。 下面是一個例子:
private void someMethodDeepDown() {
    while (.. long running task .. ) {
          ... do lots of work ...

          if (Thread.interrupted()) {
             // oh no! an interrupt!
             Thread.currentThread().interrupt();
             throw new SomeOtherException();
          }
     }
}

現在異常可以傳播或終止線程或被捕獲,但接收代碼有望注意到中斷正在進行中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM