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
  • 當使用Autofac處理一個介面有多個實現的情況時,通常會使用鍵(key)進行區分或者通過IIndex索引註入,也可以通過IEnumerable集合獲取所有實例,以下是一個具體的例子,演示如何在Autofac中註冊多個實現,並通過構造函數註入獲取指定實現。 首先,確保你已經安裝了Autofac Nu ...
  • 本篇將分享Prometheus+Grafana的監控平臺搭建,並監控之前文章所搭建的主機&服務,分享日常使用的一些使用經驗本篇將配置常用服務的監控與面板配置:包括 MySQL,MongoDB,CLickHouse,Redis,RabbitMQ,Linux,Windows,Nginx,站點訪問監控,已... ...
  • 使用Aspirate可以將Aspire程式部署到Kubernetes 集群 工具安裝 dotnet tool install -g aspirate --prerelease 註意:Aspirate 正在開發中,該軟體包將作為預覽版進行版本控制,--prelease 選項將獲得最新的預覽版。 容器註 ...
  • 前言 本文要說的這種開發模式,這種模式並不是只有blazor支持,js中有一樣的方案next.js nuxt.js;blazor還有很多其它內容,本文近關註漸進式開發模式。 是的,前後端是主流,不過以下情況也許前後端分離並不是最好的選擇: 小公司,人員不多,利潤不高,創業階段能省則省 個人開發者,接 ...
  • 在.NET中,Microsoft.Extensions.Logging是一個靈活的日誌庫,它允許你將日誌信息記錄到各種不同的目標,包括資料庫。在這個示例中,我將詳細介紹如何使用Microsoft.Extensions.Logging將日誌保存到MySQL資料庫。我們將使用Entity Framewo ...
  • chatgpt介面開發筆記3: 語音識別介面 1.文本轉語音 1、瞭解介面參數 介面地址: POST https://api.openai.com/v1/audio/speech 下麵是介面文檔描述內容: 參數: { "model": "tts-1", "input": "你好,我是饒坤,我是ter ...
  • 前面兩篇文章主要是介紹瞭如何解決高併發情況下資源爭奪的問題。但是現實的應用場景中除了要解決資源爭奪問題,高併發的情況還需要解決更多問題,比如快速處理業務數據等, 本篇文章簡要羅列一下與之相關的更多技術細節。 1、非同步編程:使用async和await關鍵字進行非同步編程,這可以避免阻塞線程,提高程式的響 ...
  • 大家好,我是棧長。 Nacos 2.3.0 前幾天正式發佈了,新增了不少實用性的新功能,真是史上最強版本。 Nacos 2.3.0 還真是一個比較重要的大版本,因為它涉及了太多重大更新,今天棧長給大家來解讀下。 Nacos 先掃個盲: Nacos 一個用於構建雲原生應用的動態服務發現、配置管理和服務 ...
  • IDEA的遠程開發功能,可以將本地的編譯、構建、調試、運行等工作都放在遠程伺服器上執行,而本地僅運行客戶端軟體進行常規的開發操作即可,舊版本IDEA目前不支持該功能.,本例使用的是IDEA2023.2.5版本 下麵介紹如何在IDEA中設置遠程連接伺服器開發環境並結合Cpolar內網穿透工具實現無公網 ...
  • 本文解釋為啥會有響應式編程,為什麼它在開發者中不太受歡迎,以及引入 Java 虛擬線程後它可能最終會消失。 命令式風格編程一直深受開發者喜愛,如 if-then-else、while 迴圈、函數和代碼塊等結構使代碼易理解、調試,異常易追蹤。然而,像所有好的東西一樣,通常也有問題。這種編程風格導致線程 ...