Callable介面和Future介面

来源:https://www.cnblogs.com/changming06/archive/2023/11/20/17845209.html
-Advertisement-
Play Games

Callable介面和Future介面 創建線程的方式 1.繼承Thread類2.實現Runnable介面3.Callable介面4.線程池方式 Callable介面 在繼承Thread類和實現Runnable介面的方式創建線程時,線程執行的run方法中,返回值是void,即無法返回線程的執行結果, ...


Callable介面和Future介面

創建線程的方式

1.繼承Thread類2.實現Runnable介面3.Callable介面4.線程池方式

Callable介面

在繼承Thread類和實現Runnable介面的方式創建線程時,線程執行的run方法中,返回值是void,即無法返回線程的執行結果,為了支持該功能,java提供了Callable介面。

Callable和Runnable介面的區別

  • 1.Callable中的call()方法,可以返回值,Runnable介面中的run方法,無返回值(void)。
  • 2.call()方法可以拋出異常,run()不能拋異常。
  • 3.實現Callable介面,必須重寫call()方法,run是抽象方法,抽象類可以不實現。
  • 4.不能用Callable介面的對象,直接替換Runnable介面對象,創建線程。

Future介面

當call()方法完成時,結果必須存儲在主線程已知的對象中,以便主線程可以知道線程返回的結果,可以使用Future對象。將Future視為保存結果的對象-該對象,不一定會將call介面返回值,保存到主線程中,但是會持有call返回值。Future基本上是主線程可以跟蹤以及其他線程的結果的一種方式。要實現此介面,必須重寫5個方法。

public interface Future<V> {//Future源碼
    /**
     * Attempts to cancel execution of this task.  This attempt will
     * fail if the task has already completed, has already been cancelled,
     * or could not be cancelled for some other reason. If successful,
     * and this task has not started when {@code cancel} is called,
     * this task should never run.  If the task has already started,
     * then the {@code mayInterruptIfRunning} parameter determines
     * whether the thread executing this task should be interrupted in
     * an attempt to stop the task.
     *
     * <p>After this method returns, subsequent calls to {@link #isDone} will
     * always return {@code true}.  Subsequent calls to {@link #isCancelled}
     * will always return {@code true} if this method returned {@code true}.
     *
     * @param mayInterruptIfRunning {@code true} if the thread executing this
     * task should be interrupted; otherwise, in-progress tasks are allowed
     * to complete
     * @return {@code false} if the task could not be cancelled,
     * typically because it has already completed normally;
     * {@code true} otherwise
     */
    boolean cancel(boolean mayInterruptIfRunning);
    //用於停止任務,如果未啟動,將停止任務,已啟動,僅在mayInterrupt為true時才會中斷任務

    /**
     * Returns {@code true} if this task was cancelled before it completed
     * normally.
     *
     * @return {@code true} if this task was cancelled before it completed
     */
    boolean isCancelled();

    /**
     * Returns {@code true} if this task completed.
     *
     * Completion may be due to normal termination, an exception, or
     * cancellation -- in all of these cases, this method will return
     * {@code true}.
     *
     * @return {@code true} if this task completed
     */
    boolean isDone();//判斷任務是否完成,完成true,未完成false

    /**
     * Waits if necessary for the computation to complete, and then
     * retrieves its result.
     *
     * @return the computed result
     * @throws CancellationException if the computation was cancelled
     * @throws ExecutionException if the computation threw an
     * exception
     * @throws InterruptedException if the current thread was interrupted
     * while waiting
     */
    V get() throws InterruptedException, ExecutionException;//任務完成返回結果,未完成,等待完成

    /**
     * Waits if necessary for at most the given time for the computation
     * to complete, and then retrieves its result, if available.
     *
     * @param timeout the maximum time to wait
     * @param unit the time unit of the timeout argument
     * @return the computed result
     * @throws CancellationException if the computation was cancelled
     * @throws ExecutionException if the computation threw an
     * exception
     * @throws InterruptedException if the current thread was interrupted
     * while waiting
     * @throws TimeoutException if the wait timed out
     */
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

Callable和Future是分別做各自的事情,Callable和Runnable類似,因為它封裝了要在另一個線程上允許的任務,而Future用於存儲從另一個線程獲取的結果。實際上,Future和Runnable可以一起使用。創建線程需要Runnable,為了獲取結果,需要Future。

FutureTask

Java庫本身提供了具體的FutureTask類,該類實現了Runnable和Future介面,並方便的將兩種功能組合在一起。並且其構造函數提供了Callable來創建FutureTask對象。然後將該FutureTask對象提供給Thread的構造函數以創建Thread對象。因此,間接的使用Callable創建線程。

關於FutureTask構造函數的理解
public class FutureTask<V> implements RunnableFuture<V> {//實現了RunnableFuture
	//該類的構造函數有兩個
    public FutureTask(Callable<V> callable) {//
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
    //用此方法創建的線程,最後start()調用的其實是Executors.RunnableAdapter類的call(); 
    public FutureTask(Runnable runnable, V result) {//返回的結果,就是構造函數傳入的值
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }
    //原因如下
    //使用FutureTask對象的方式,創建的Thread,在開啟線程.start後,底層執行的是
   //Thread的方法 start()方法會調用start0(),但是調用這個start0()(操作系統創建線程)的時機,是操作系統決定的,但是這個start0()最後會調用run()方法,此線程的執行內容就在傳入的對象的run()方法中
    public void run() {
        if (target != null) {
            target.run();
            //執行到這 target就是在創建Thread時,傳遞的Runnable介面的子類對象,FUtureTask方式就是傳入的FutureTask對象
            //會執行FutureTask中的run()方法
        }
    }
    //FutureTask的run()
    public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;//類的屬性
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    //會執行callable的call(),callable是創建TutureTask時,根據傳入的Runnable的對象封裝後的一個對象
                    //this.callable = Executors.callable(runnable, result);
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }
    //Executors類的callable
    public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<T>(task, result);
    }
    //最後執行到這裡 Executors類的靜態內部類RunnableAdapter
    static final class RunnableAdapter<T> implements Callable<T> {
        final Runnable task;
        final T result;
        RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
        }
        public T call() {
            task.run();
            return result;
        }
    }
}
public interface RunnableFuture<V> extends Runnable, Future<V> {//繼承了Runnable, Future<V>介面
	void run();//
}
使用FutureTask類間接的用Callable介面創建線程
/**
 * @author 長名06
 * @version 1.0
 * 演示Callable創建線程 需要使用到Runnable的實現類FutureTask類
 */
public class CallableDemo {
    public static void main(String[] args) {
        FutureTask<Integer> futureTask = new FutureTask<>(() -> {
            System.out.println(Thread.currentThread().getName() +  "使用Callable介面創建的線程");
            return 100;
        });

        new Thread(futureTask,"線程1").start();
    }
}
核心原理

在主線程中需要執行耗時的操作時,但不想阻塞主線程,可以把這些作業交給Future對象來做。

  • 1.主線程需要,可以通過Future對象獲取後臺作業的計算結果或者執行狀態。
  • 2.一般FutureTask多用於耗時的計算,主線程可以在完成自己的任務後,再獲取結果。
  • 3.只有執行完後,才能獲取結果,否則會阻塞get()方法。
  • 4.一旦執行完後,不能重新開始或取消計算。get()只會計算一次

小結

  • 1.在主線程需要執行比較耗時的操作時,但又不想阻塞主線程,可以把這些作業交給Future對象在後臺完成,當主線程將來需要時,就可以通過Future對象獲得後臺作業的計算結果或者執行狀態。
  • 2.一般FutureTask多用於耗時的計算,主線程可以在完成自己的任務後,再去獲取結果。
  • 3.get()方法只能獲取結果,並且只能計算完後獲取,否則會一直阻塞直到任務轉入完成狀態,然後會返回結果或拋出異常。且只計算一次,計算完成不能重新開始或取消計算。
    只是為了記錄自己的學習歷程,且本人水平有限,不對之處,請指正。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 可以少去理解一些不必要的概念,而多去思考為什麼會有這樣的東西,它解決了什麼問題,或者它的運行機制是什麼? 1. React 中導出和導入 1.1 ES6 解析 ES6 的模塊化的基本規則或特點: 每一個模塊只載入一次, 每一個 JS 只執行一次, 如果下次再去載入同目錄下同文件,直接從記憶體中讀取。一 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 效果 金幣從初始位置散開後逐個飛向指定位置,這是游戲中很常用的一個動畫,效果如下: 思路 這個效果中,分成兩個階段: 一定數量的金幣從一個起點散開 這些金幣逐一飛向終點 計算金幣的初始散開位置 生成圓周上的等分點 金幣散開的位置看似隨機, ...
  • 古詩文起名 大家好,我是 Java陳序員,我們常常會為了給孩子取名而煩惱,取名不僅要好聽而且要規避大眾化。其實,我們中華文化博大精深,可以借鑒先輩文人們留下的經典詩詞中的文字來起名。今天,給大家介紹一個古詩文起名的工具。 這個工具支持從《詩經》、《楚辭》、《唐詩》、《宋詞》、《樂府詩集》、《古詩三百 ...
  • SubScribe即發佈訂閱模式,在工作中有著廣泛的應用,比如跨組件通信,微前端系統中跨子應用通信等等。 以下是一個簡易的實現: 訂閱 初始化時可限制類型 發佈 限制類型是為了讓訂閱者和發佈者知道預製了哪些類型,避免使用了一些對方不知道的類型。 type Subscriber<T> = (param ...
  • 使用集團的統一埋點採集能力和埋點平臺,完成達達7條業務線共43個站點應用的埋點遷移,降低自研採集工具和平臺的研發投入和機器成本,打通數據鏈路,創造更多的數據分析價值 ...
  • 大家好,我是Java陳序員。 我們在日常開發中,會有很多的應用環境,開發環境、測試環境、回歸環境、生產環境等等。 這些環境,需要部署在一臺台的伺服器上,有的可能是物理機,有的可能是雲伺服器。 那麼,這麼多主機我們要怎麼運維整理呢? 今天,給大家介紹一個輕量級的自動化運維平臺。 項目介紹 Spug—— ...
  • Vue3中響應數據核心是 reactive , reactive 中的實現是由 proxy 加 effect 組合,我們先來看一下 reactive 方法的定義 ...
  • 背景 近期查看公司項目的請求日誌,發現有一段來自俄羅斯首都莫斯科(根據IP是這樣,沒精力溯源)的異常請求,看傳參就能猜到是EXP攻擊,不是瞎掃描瞎傳參的那種。日誌如下(已做部分修改): [2023-11-17 23:54:34] local.INFO: url : http://xxx/_ignit ...
一周排行
    -Advertisement-
    Play Games
  • 前言 插件化的需求主要源於對軟體架構靈活性的追求,特別是在開發大型、複雜或需要不斷更新的軟體系統時,插件化可以提高軟體系統的可擴展性、可定製性、隔離性、安全性、可維護性、模塊化、易於升級和更新以及支持第三方開發等方面的能力,從而滿足不斷變化的業務需求和技術挑戰。 一、插件化探索 在WPF中我們想要開 ...
  • 歡迎ReaLTaiizor是一個用戶友好的、以設計為中心的.NET WinForms項目控制項庫,包含廣泛的組件。您可以使用不同的主題選項對項目進行個性化設置,並自定義用戶控制項,以使您的應用程式更加專業。 項目地址:https://github.com/Taiizor/ReaLTaiizor 步驟1: ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • Channel 是乾什麼的 The System.Threading.Channels namespace provides a set of synchronization data structures for passing data between producers and consume ...
  • efcore如何優雅的實現按年分庫按月分表 介紹 本文ShardinfCore版本 本期主角: ShardingCore 一款ef-core下高性能、輕量級針對分表分庫讀寫分離的解決方案,具有零依賴、零學習成本、零業務代碼入侵適配 距離上次發文.net相關的已經有很久了,期間一直在從事java相關的 ...
  • 前言 Spacesniffer 是一個免費的文件掃描工具,通過使用樹狀圖可視化佈局,可以立即瞭解大文件夾的位置,幫助用戶處理找到這些文件夾 當前系統C盤空間 清理後系統C盤空間 下載 Spacesniffer 下載地址:https://spacesniffer.en.softonic.com/dow ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • 一、ReZero簡介 ReZero是一款.NET中間件 : 全網唯一開源界面操作就能生成API , 可以集成到任何.NET6+ API項目,無破壞性,也可讓非.NET用戶使用exe文件 免費開源:MIT最寬鬆協議 , 一直從事開源事業十年,一直堅持開源 1.1 純ReZero開發 適合.Net Co ...
  • 一:背景 1. 講故事 停了一個月沒有更新文章了,主要是忙於寫 C#內功修煉系列的PPT,現在基本上接近尾聲,可以回頭繼續更新這段時間分析dump的一些事故報告,有朋友微信上找到我,說他們的系統出現了大量的http超時,程式不響應處理了,讓我幫忙看下怎麼回事,dump也抓到了。 二:WinDbg分析 ...
  • 開始做項目管理了(本人3年java,來到這邊之後真沒想到...),天天開會溝通整理需求,他們講話的時候忙裡偷閑整理一下常用的方法,其實語言還是有共通性的,基本上看到方法名就大概能猜出來用法。出去打水的時候看到外面太陽好好,真想在外面坐著曬太陽,回來的時候好兄弟三年前送給我的鍵盤D鍵不靈了,在打"等待 ...