1. 程式人生 > >Java 多執行緒設定執行緒超時時間之 Callable介面和Future介面

Java 多執行緒設定執行緒超時時間之 Callable介面和Future介面

Callable介面和Future介面介紹

        在Java中,如果需要設定程式碼執行的最長時間,即超時,可以用Java執行緒池ExecutorService類配合Future介面來實現。 Future介面是Java標準API的一部分,在java.util.concurrent包中。Future介面是Java執行緒Future模式的實現,可以來進行非同步計算。

        Future模式可以這樣來描述:我有一個任務,提交給了Future,Future替我完成這個任務。期間我自己可以去做任何想做的事情。一段時間之後,我就便可以從Future那兒取出結果。就相當於下了一張訂貨單,一段時間後可以拿著提訂單來提貨,這期間可以幹別的任何事情。其中Future 介面就是訂貨單,真正處理訂單的是Executor類,它根據Future介面的要求來生產產品。

        Future介面提供方法來檢測任務是否被執行完,等待任務執行完獲得結果,也可以設定任務執行的超時時間。這個設定超時的方法就是實現Java程式執行超時的關鍵。

        Future介面是一個泛型介面,嚴格的格式應該是Future<V>,其中V代表了Future執行的任務返回值的型別。 Future介面的方法介紹如下:

  • boolean cancel (boolean mayInterruptIfRunning) 取消任務的執行。引數指定是否立即中斷任務執行,或者等等任務結束
  • boolean isCancelled () 任務是否已經取消,任務正常完成前將其取消,則返回 true
  • boolean isDone () 任務是否已經完成。需要注意的是如果任務正常終止、異常或取消,都將返回true
  • get () throws InterruptedException, ExecutionException  等待任務執行結束,然後獲得V型別的結果。InterruptedException 執行緒被中斷異常, ExecutionException任務執行異常,如果任務被取消,還會丟擲CancellationException
  • get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 同上面的get功能一樣,多了設定超時時間。引數timeout指定超時時間,uint指定時間的單位,在列舉類TimeUnit中有相關的定義。如果計算超時,將丟擲TimeoutException

         Future的實現類有java.util.concurrent.FutureTask<V>即 javax.swing.SwingWorker<T,V>。通常使用FutureTask來處理我們的任務。FutureTask類同時又實現了Runnable介面,所以可以直接提交給Executor執行。使用FutureTask實現超時執行的程式碼如下:

  1. ExecutorService executor = Executors.newSingleThreadExecutor();  
  2. FutureTask<String> future =  
  3.        new FutureTask<String>(new Callable<String>() {//使用Callable介面作為構造引數
  4.          public String call() {  
  5.            //真正的任務在這裡執行,這裡的返回值型別為String,可以為任意型別
  6.        }});  
  7. executor.execute(future);  
  8. //在這裡可以做別的任何事情
  9. try {  
  10.     result = future.get(5000, TimeUnit.MILLISECONDS); //取得結果,同時設定超時執行時間為5秒。同樣可以用future.get(),不設定執行超時時間取得結果
  11. catch (InterruptedException e) {  
  12.     futureTask.cancel(true);  
  13. catch (ExecutionException e) {  
  14.     futureTask.cancel(true);  
  15. catch (TimeoutException e) {  
  16.     futureTask.cancel(true);  
  17. finally {  
  18.     executor.shutdown();  
  19. }  
       不直接構造Future物件,也可以使用ExecutorService.submit方法來獲得Future物件,submit方法即支援以 Callable介面型別,也支援Runnable介面作為引數,具有很大的靈活性。使用示例如下:
  1. ExecutorService executor = Executors.newSingleThreadExecutor();  
  2. FutureTask<String> future = executor.submit(  
  3.    new Callable<String>() {//使用Callable介面作為構造引數
  4.        public String call() {  
  5.       //真正的任務在這裡執行,這裡的返回值型別為String,可以為任意型別
  6.    }});  
  7. //在這裡可以做別的任何事情
  8. //同上面取得結果的程式碼
        利用Future介面實現程式執行超時大致用法就這麼多,改天需要研究下Future介面的內部實現,特別是設定執行超時的實現。

例子和注意事項

例1:try catch在for迴圈之外
  1. package testexception;  
  2. import java.util.concurrent.Callable;  
  3. import java.util.concurrent.ExecutionException;  
  4. import java.util.concurrent.ExecutorService;  
  5. import java.util.concurrent.Executors;  
  6. import java.util.concurrent.FutureTask;  
  7. import java.util.concurrent.TimeUnit;  
  8. import java.util.concurrent.TimeoutException;  
  9. publicclass FutureTaskTest{  
  10.     publicstaticvoid main(String[] args) {  
  11.         ExecutorService executor = Executors.newSingleThreadExecutor();  
  12.         FutureTask<String> futureTask =  
  13.                new FutureTask<String>(new Callable<String>() {//使用Callable介面作為構造引數
  14.                  public String call() {  
  15.                      try {  
  16.                          for (int i = 0; i < 5; i++) {  
  17.                              Thread.sleep(1000);  
  18.                              System.out.println("------------------------"+i);  
  19.                         }  
  20.                     } catch (InterruptedException e) {  
  21.                         System.out.println("InterruptedException1111111");  
  22.                         e.printStackTrace();  
  23.                     }  
  24.                    //真正的任務在這裡執行,這裡的返回值型別為String,可以為任意型別
  25.                     return"call result";  
  26.                }});  
  27.         executor.execute(futureTask);  
  28.         //在這裡可以做別的任何事情
  29.         try {  
  30.             String result = futureTask.get(3000, TimeUnit.MILLISECONDS);   
  31.             //取得結果,同時設定超時執行時間為5秒。同樣可以用future.get(),不設定執行超時時間取得結果
  32.             System.out.println("___________"+result+"_______________");  
  33.         } catch (InterruptedException e) {  
  34.             System.out.println("InterruptedException222222");  
  35.             futureTask.cancel(true);  
  36.         } catch (ExecutionException e) {  
  37.             System.out.println("InterruptedException333333333");  
  38.             futureTask.cancel(true);  
  39.         } catch (TimeoutException e) {  
  40.             System.out.println("!!!!!!!!Time out!!!!!!!!!!");  
  41.             futureTask.cancel(true);  
  42.         } finally {  
  43.             executor.shutdown();  
  44.         }  
  45.     }  
  46. }  

輸出結果: ------------------------0
------------------------1 ------------------------2
!!!!!!!!Time out!!!!!!!!!!
InterruptedException1111111
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at testexception.FutureTaskTest$1.call(FutureTaskTest.java:20)
at testexception.FutureTaskTest$1.call(FutureTaskTest.java:1)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)

        說明:3秒之內沒有執行完所以拋了TimeoutException異常,並取消了執行緒,可以看到後面就沒有輸出3,4了。注意這裡call()函式中,try catch在for迴圈之外,這樣執行結果是沒有問題的,是我們想要的結果;但是,如果把try catch寫在了for迴圈之內,結果會怎樣呢,請看下例。另外,result值即為call()函式返回的值本例中為“call result”。

例子2:try catch在for迴圈之內

  1. package testexception;  
  2. import java.util.concurrent.Callable;  
  3. import java.util.concurrent.ExecutionException;  
  4. import java.util.concurrent.ExecutorService;  
  5. import java.util.concurrent.Executors;  
  6. import java.util.concurrent.FutureTask;  
  7. import java.util.concurrent.TimeUnit;  
  8. import java.util.concurrent.TimeoutException;  
  9. publicclass FutureTaskTest{  
  10.     publicstaticvoid main(String[] args) {  
  11.         ExecutorService executor = Executors.newSingleThreadExecutor();  
  12.         FutureTask<String> futureTask =  
  13.                new FutureTask<String>(new Callable<String>() {//使用Callable介面作為構造引數
  14.                  public String call() {  
  15.                      for (int i = 0; i < 5; i++) {  
  16.                          try {  
  17.                                  Thread.sleep(1000);  
  18.                                  System.out.println("------------------------"+i);  
  19.                             } catch (InterruptedException e) {  
  20.                                 System.out.println("InterruptedException1111111");  
  21.                                 e.printStackTrace();  
  22.                             }  
  23.                      }  
  24.                    //真正的任務在這裡執行,這裡的返回值型別為String,可以為任意型別
  25.                     return"call result";  
  26.                }});  
  27.         executor.execute(futureTask);  
  28.         //在這裡可以做別的任何事情
  29.         try {  
  30.             String result = futureTask.get(3000, TimeUnit.MILLISECONDS);   
  31.             //取得結果,同時設定超時執行時間為5秒。同樣可以用future.get(),不設定執行超時時間取得結果
  32.             System.out.println("___________"+result+"_______________");  
  33.         } catch (InterruptedException e) {  
  34.             System.out.println("InterruptedException222222");  
  35.             futureTask.cancel(true);  
  36.         } catch (ExecutionException e) {  
  37.             System.out.println("InterruptedException333333333");  
  38.             futureTask.cancel(true);  
  39.         } catch (TimeoutException e) {  
  40.             System.out.println("!!!!!!!!Time out!!!!!!!!!!");  
  41.             futureTask.cancel(true);  
  42.         } finally {  
  43.             executor.shutdown();  
  44.         }  
  45.     }  
  46. }  

輸出結果:

------------------------0
------------------------1
------------------------2
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
!!!!!!!!Time out!!!!!!!!!!
InterruptedException1111111
at testexception.FutureTaskTest$1.call(FutureTaskTest.java:20)
at testexception.FutureTaskTest$1.call(FutureTaskTest.java:1)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)
------------------------4

        說明:根據輸出結果可以看到只有執行緒3被中斷了,後面執行緒並沒有按照我們希望的超時之後就停下來,即使我們在超時異常裡面取消了執行緒。也就是說,try catch在for迴圈之內時,取消執行緒後會讓執行緒中斷,所以第四次迴圈未執行完即被中斷,丟擲中斷異常,然而這個中斷異常被for迴圈之內的執行緒中斷異常catch捕獲到,所以它仍會繼續後面的迴圈輸出。但其實現在的執行緒已經被標識為中斷了,如果你在超時異常catch塊中輸出futureTask.isCancelled(),你會發現返回的是true。使用時要特別注意這種容易出錯的情況

相關推薦

Java 執行設定執行超時時間 Callable介面Future介面 超時控制

Callable介面和Future介面介紹         在Java中,如果需要設定程式碼執行的最長時間,即超時,可以用Java執行緒池ExecutorService類配合Future介面來實現。 Future介面是Java標準API的一部分,在java.uti

Java 執行設定執行超時時間 Callable介面Future介面

Callable介面和Future介面介紹         在Java中,如果需要設定程式碼執行的最長時間,即超時,可以用Java執行緒池ExecutorService類配合Future介面來實現。 Future介面是Java標準API的一部分,在java.util.co

java執行設定 執行超時 非阻塞實現

執行緒是屬於非同步計算模型,所以你不可能直接從別的執行緒中得到函式返回值。 這時候,Future就出場了。Futrue可以監視目標執行緒呼叫call的情況,當你呼叫Future的get()方法以獲得結果時,當前執行緒就開始阻塞,直接call方法結束返回結果。

java個例項物件執行同步無效的原因以及解決方案

【原因】:synchronized用法沒搞清楚 下面就直接舉例子了! 【情況1】:單例項物件多執行緒 public class Demo3 { Tlwindow twd1,twd2,twd3; //定義三個視窗 public static void

Java執行CallableFuture介面的實現

Callable和Future     Callable介面定義了一個call方法可以作為執行緒的執行體,但call方法比run方法更強大:     A、call方法可以有返回值     B、call方法可以申明丟擲異常       Callable介面是JDK5後

iOS 執行 設定執行順序

////  ViewController.m//  GCDDownload////  Created by ql on 2018/4/4.//  Copyright © 2018年 carystaloptech. All rights reserved.//#import "

實現執行得兩種方法:Thread類整合Runnable介面實現

建立執行緒的第一種方式:繼承Thread類。 步驟: 1,定義類繼承Thread。 2,複寫Thread類中的run方法。 目的:將自定義程式碼儲存在run方法。讓執行緒執行。//run();僅僅是物件呼叫方法。而執行緒建立了,並沒有執行。 3,呼叫執行緒的start方法, 該方法兩個作用:啟動執行緒,呼

實現執行的第三種方式Callable

中說過,實現多執行緒有兩種方式:一種是繼承Thread類,另一種是實現Runnable介面。這兩種方式中真正起作用的是run方法,不過run方法並沒有返回值。如果我們希望任務在完成時能夠有返回值,這時就

執行實現callable介面runnable介面的區別

Callable和Runnable的區別: 返回值:Callable有返回值,Runnable沒有返回值 異常:Runnable沒有容錯機制,意味著如果出現異常必須立即處理;Callable有容錯機制,意味著出現異常之後可以向上丟擲 啟動方式:Runnable可以通過

執行的兩種建立方式:Thread類Runnable介面,它們都要複寫run方法

/*執行緒: * 1.如何在自定義的程式碼中,自定義一個執行緒呢? * * 通過對API的查詢,java已提供了對執行緒這類事物的描述。就是Thread類 * * 建立執行緒的第一種方式:繼承Thread類。 * 步驟: * 1.定義類繼承Thread。

執行池 一、executor介面ExecutorService介面介紹

工作這麼多年,很少有時間寫部落格,昨天和一個正在跳槽找工作的同學交流,他是做web的,面試的時候被問到了執行緒池一塊的技術,被難住了!這讓我不禁也想鞏固下我這方便的基礎了,天天在用的東西,尤其是像我們這種做網際網路服務端開發的,高併發處理中建立一個優異Thread Pool對

啟動執行的第三種方式Callable

java中第三種啟動執行緒的方式是實現Callable介面package com.lyzx.juc; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException;

Java線程系列---“JUC原子類”04 AtomicLongArray原子類

ray wan 組復制 set pri spa 設置 gif 數組類 轉自:https://www.cnblogs.com/skywang12345/p/3514604.html(含部分修改) 概要 AtomicIntegerArray, AtomicLongArray,

Java線程系列--“JUC原子類”03 AtomicLong原子類

給定 延時 integer 待修改 java源碼 用法 int() cde aml 轉自:https://www.cnblogs.com/skywang12345/p/3514593.html(含部分修改) 概要 AtomicInteger, AtomicLong和Atom

Java線程系列---“JUC原子類”05 AtomicReference原子類

系列 void test sys 源碼 lap ava 線程 http 轉自:http://www.cnblogs.com/skywang12345/p/3514623.html(部分修改) 概要 本章對AtomicReference引用類型的原子類進行介紹。內容包括:

Java線程系列---“JUC原子類”06 AtomicLongFieldUpdater原子類

添加 bstr pro 源碼 set 當前 span value .com 轉自:http://www.cnblogs.com/skywang12345/p/3514635.html (含部分修改) 概要 AtomicIntegerFieldUpdater, AtomicL

Socket 關於設定Socket連線超時時間

做網路程式設計的人對setSoTimeout方法一定很熟悉,都知道是設定連線的超時時間! 但是我在網上找資料時發現很多人把這個超時時間理解成了鏈路的超時時間!我看了一下JDK 關於這個方法的說明,其實根本不是鏈路的超時時間! Java程式碼   setSoTimeout   public vo

設定TCP connect超時時間的2種方法

1.常用方法設定socket非阻塞,之後使用select等設定超時時間2.使用alarm訊號量需要注意:執行緒訊號量掩碼是執行緒私有的,當指定程序遞交訊號量時,作業系統會將訊號量遞交至該程序中未遮蔽該訊號量的所有執行緒中的隨機之一。見 man 7 signal:Asignal

android retrofit設定網路請求超時時間

今天開發的時候遇到一個網路請求超時的問題,後臺處理是成功的,但是移動端返回的總是提示請求超時,在設定了retrofit請求超時的時間延長以後,就可以請求成功了,下面是配置的方法: private static final OkHttpClient client =

如何設定頁面載入超時時間,robotframework+selenium實現

如何設定頁面載入超時時間,robotframework+selenium實現,設定瀏覽器的超時時間,自身載入頁面的超時時間   有些測試,使用的方法是如下場景,例如: 訪問google超時,由於無法訪問,想在10秒內退出,要不然可能登登上1分鐘以上 我們想加快這個進度,