国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

FutureTask源碼解析(1)——預(yù)備知識

mmy123456 / 1756人閱讀

摘要:在分析它的源碼之前我們需要先了解一些預(yù)備知識。因為接口沒有返回值所以為了與兼容我們額外傳入了一個參數(shù)使得返回的對象的方法直接執(zhí)行的方法然后返回傳入的參數(shù)。

前言

系列文章目錄

FutureTask 是一個同步工具類,它實現(xiàn)了Future語義,表示了一種抽象的可生成結(jié)果的計算。在包括線程池在內(nèi)的許多工具類中都會用到,弄懂它的實現(xiàn)將有利于我們更加深入地理解Java異步操作實現(xiàn)。

在分析它的源碼之前, 我們需要先了解一些預(yù)備知識。本篇我們先來看看FutureTask 中所使用到的接口:RunnableCallableFutureRunnableFuture以及所使用到的工具類ExecutorsUnsafe

FutureTask所使用到的接口 Runnable接口

在前面Thread類源碼解讀的系列文章中我們說過, 創(chuàng)建線程最重要的是傳遞一個run()方法, 這個run方法定義了這個線程要做什么事情, 它被抽象成了Runnable接口:

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

但是, 可以發(fā)現(xiàn), 這個方法并沒有任何返回值.
如果我們希望執(zhí)行某種類型的操作并拿到它的執(zhí)行結(jié)果, 該怎么辦呢?

從 Runnable 到 Callable

要從某種類型的操作中拿到執(zhí)行結(jié)果, 最簡單的方式自然是令這個操作自己返回操作結(jié)果, 則相較于run方法返回void,我們可以令一個操作返回特定類型的對象, 這種思路的實現(xiàn)就是Callable接口:

@FunctionalInterface
public interface Callable {
    V call() throws Exception;
}

對比Callable接口與Runnable接口, 我們可以發(fā)現(xiàn)它們最大的不同點在于:

Callable有返回值

Callable可以拋出異常

關(guān)于有返回值這點,我們并不意外,因為這就是我們的需求,call方法的返回值類型采用的泛型,該類型是我們在創(chuàng)建Callable對象的時候指定的。

除了有返回值外,相較于Runnable接口,Callable還可以拋出異常,這點看上去好像沒啥特別的,但是卻有大用處——這意味著如果在任務(wù)執(zhí)行過程中發(fā)生了異常,我們可以將它向上拋出給任務(wù)的調(diào)用者來妥善處理,我們甚至可以利用這個特性來中斷一個任務(wù)的執(zhí)行。而Runnable接口的run方法不能拋出異常,只能在方法內(nèi)部catch住處理,喪失了一定的靈活性。

使用Callable接口解決了返回執(zhí)行結(jié)果的問題, 但是也帶來了一個新的問題:

如何獲得執(zhí)行結(jié)果?

有的同學(xué)可能就要說了, 這還不簡單? 直接拿不就好了, 看我的:

public static void main(String[] args) {
    Callable myCallable = () -> "This is the results.";
    try {
        String result = myCallable.call();
        System.out.println("Callable 執(zhí)行的結(jié)果是: " + result);
    } catch (Exception e) {
        System.out.println("There is a exception.");
    }
}

這種方法確實可以, 但是它存在幾個問題:

call方法是在當(dāng)前線程中直接調(diào)用的, 無法利用多線程。

call方法可能是一個特別耗時的操作, 這將導(dǎo)致程序停在myCallable.call()調(diào)用處, 無法繼續(xù)運行, 直到call方法返回。

如果call方法始終不返回, 我們沒辦法中斷它的運行。

因此, 理想的操作應(yīng)當(dāng)是, 我們將call方法提交給另外一個線程執(zhí)行, 并在合適的時候, 判斷任務(wù)是否完成, 然后獲取線程的執(zhí)行結(jié)果或者撤銷任務(wù), 這種思路的實現(xiàn)就是Future接口:

Future接口

Future接口被設(shè)計用來代表一個異步操作的執(zhí)行結(jié)果。你可以用它來獲取一個操作的執(zhí)行結(jié)果、取消一個操作、判斷一個操作是否已經(jīng)完成或者是否被取消

public interface Future {
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
    
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    
    boolean isDone();
}

Future接口一共定義了5個方法:

get()

該方法用來獲取執(zhí)行結(jié)果, 如果任務(wù)還在執(zhí)行中, 就阻塞等待;

get(long timeout, TimeUnit unit)

該方法同get方法類似, 所不同的是, 它最多等待指定的時間, 如果指定時間內(nèi)任務(wù)沒有完成, 則會拋出TimeoutException異常;

cancel(boolean mayInterruptIfRunning)

該方法用來嘗試取消一個任務(wù)的執(zhí)行, 它的返回值是boolean類型, 表示取消操作是否成功.

isCancelled()

該方法用于判斷任務(wù)是否被取消了。如果一個任務(wù)在正常執(zhí)行完成之前被cancel掉了, 則返回true

isDone()

如果一個任務(wù)已經(jīng)結(jié)束, 則返回true。注意, 這里的任務(wù)結(jié)束包含了以下三種情況:

任務(wù)正常執(zhí)行完畢

任務(wù)拋出了異常

任務(wù)已經(jīng)被取消

關(guān)于cancel方法,這里要補充說幾點:
首先有以下三種情況之一的,cancel操作一定是失敗的:

任務(wù)已經(jīng)執(zhí)行完成了

任務(wù)已經(jīng)被取消過了

任務(wù)因為某種原因不能被取消

其它情況下,cancel操作將返回true。值得注意的是,cancel操作返回true并不代表任務(wù)真的就是被取消了,這取決于發(fā)動cancel狀態(tài)時任務(wù)所處的狀態(tài):

如果發(fā)起cancel時任務(wù)還沒有開始運行,則隨后任務(wù)就不會被執(zhí)行;

如果發(fā)起cancel時任務(wù)已經(jīng)在運行了,則這時就需要看mayInterruptIfRunning參數(shù)了:

如果mayInterruptIfRunning 為true, 則當(dāng)前在執(zhí)行的任務(wù)會被中斷

如果mayInterruptIfRunning 為false, 則可以允許正在執(zhí)行的任務(wù)繼續(xù)運行,直到它執(zhí)行完

這個cancel方法的規(guī)范看起來有點繞,現(xiàn)在不太理解沒關(guān)系,后面結(jié)合實例去看就容易弄明白了,我們將在下一篇分析FutureTask源碼的時候詳細說說FutureTask對這一方法的實現(xiàn)。

RunnableFuture 接口

RunnableFuture接口人如其名, 就是同時實現(xiàn)了Runnable接口和Future接口:

public interface RunnableFuture extends Runnable, Future {
    void run(); 
}

我們下一篇開始分析FutureTask的源碼的時候就將看到,F(xiàn)utureTask實現(xiàn)了該接口,也就是相當(dāng)于它同時實現(xiàn)了Runnable接口和Future接口。

有的同學(xué)可能會對這個接口產(chǎn)生疑惑,既然已經(jīng)繼承了Runnable,該接口自然就繼承了run方法,為什么要在該接口的內(nèi)部再寫一個run方法?

單純從理論上來說,這里確實是沒有必要的,再多寫一遍,我覺得大概就是為了看上去直觀一點,便于文檔或者UML圖展示。

FutureTask所使用到的工具類 Executors

Executors 是一個用于創(chuàng)建線程池的工廠類,關(guān)于線程池的概念,我們以后再說。這個類同時也提供了一些有用的靜態(tài)方法。

前面我們提到了Callable接口,它是JDK1.5才引入的,而Runnable接口在JDK1.0就有了,我們有時候需要將一個已經(jīng)存在Runnable對象轉(zhuǎn)換成Callable對象,Executors工具類為我們提供了這一實現(xiàn):

public class Executors {
    /**
     * Returns a {@link Callable} object that, when
     * called, runs the given task and returns the given result.  This
     * can be useful when applying methods requiring a
     * {@code Callable} to an otherwise resultless action.
     * @param task the task to run
     * @param result the result to return
     * @param  the type of the result
     * @return a callable object
     * @throws NullPointerException if task null
     */
    public static  Callable callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter(task, result);
    }
    
    /**
     * A callable that runs given task and returns given result
     */
    static final class RunnableAdapter implements Callable {
        final Runnable task;
        final T result;
        RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
        }
        public T call() {
            task.run();
            return result;
        }
    }
}

可以明顯看出來,這個方法采用了設(shè)計模式中的適配器模式,將一個Runnable類型對象適配成Callable類型。

因為Runnable接口沒有返回值, 所以為了與Callable兼容, 我們額外傳入了一個result參數(shù), 使得返回的Callable對象的call方法直接執(zhí)行Runnable的run方法, 然后返回傳入的result參數(shù)。

有的同學(xué)要說了, 你把result參數(shù)傳進去, 又原封不動的返回出來, 有什么意義呀?
這樣做確實沒什么意義, result參數(shù)的存在只是為了將一個Runnable類型適配成Callable類型.

Unsafe

Unsafe類對于并發(fā)編程來說是個很重要的類,如果你稍微看過J.U.C里的源碼(例如我們前面講AQS系列的文章里),你會發(fā)現(xiàn)到處充斥著這個類的方法調(diào)用。

這個類的最大的特點在于,它提供了硬件級別的CAS原子操作。

可能有的同學(xué)會覺得這并沒有什么了不起,CAS的概念都被說爛了。但是,CAS可以說是實現(xiàn)了最輕量級的鎖,當(dāng)多個線程嘗試使用CAS同時更新同一個變量時,只有其中的一個線程能成功地更新變量的值,而其他的線程將失敗。然而,失敗的線程并不會被掛起。

CAS操作包含了三個操作數(shù): 需要讀寫的內(nèi)存位置,進行比較的原值,擬寫入的新值。

在Unsafe類中,實現(xiàn)CAS操作的方法是: compareAndSwapXXX

例如:

public native boolean compareAndSwapObject(Object obj, long offset, Object expect, Object update);

obj是我們要操作的目標(biāo)對象

offset表示了目標(biāo)對象中,對應(yīng)的屬性的內(nèi)存偏移量

expect是進行比較的原值

update是擬寫入的新值。

所以該方法實現(xiàn)了對目標(biāo)對象obj中的某個成員變量(field)進行CAS操作的功能。

那么,要怎么獲得目標(biāo)field的內(nèi)存偏移量offset呢? Unsafe類為我們提供了一個方法:

public native long objectFieldOffset(Field field);

該方法的參數(shù)是我們要進行CAS操作的field對象,要怎么獲得這個field對象呢?最直接的辦法就是通過反射了:

Class k = FutureTask.class;
Field stateField = k.getDeclaredField("state");

這樣一波下來,我們就能對FutureTask的state屬性進行CAS操作了o( ̄▽ ̄)o

除了compareAndSwapObject,Unsafe類還提供了更為具體的對int和long類型的CAS操作:

public native boolean compareAndSwapInt(Object obj, long offset, int expect, int update);
public native boolean compareAndSwapLong(Object obj, long offset, long expect, long update);

從方法簽名可以看出,這里只是把目標(biāo)field的類型限定成int和long類型,而不是通用的Object.

最后,F(xiàn)utureTask還用到了一個方法:

public native void putOrderedInt(Object obj, long offset, int value);

可以看出,該方法只有三個參數(shù),所以它沒有比較再交換的概念,某種程度上就是一個賦值操作,即設(shè)置obj對象中offset偏移地址對應(yīng)的int類型的field的值為指定值。這其實是Unsafe的另一個方法putIntVolatile的有序或者有延遲的版本,并且不保證值的改變被其他線程立即看到,只有在field被volatile修飾并且期望被意外修改的時候使用才有用。

那么putIntVolatile方法的定義是什么呢?

public native void putIntVolatile(Object obj, long offset, int value);

該方法設(shè)置obj對象中offset偏移地址對應(yīng)的整型field的值為指定值,支持volatile store語義。由此可以看出,當(dāng)操作的int類型field本身已經(jīng)被volatile修飾時,putOrderedIntputIntVolatile是等價的。

好了,到這里,基本需要用到的預(yù)備知識我們都學(xué)習(xí)完了,障礙已經(jīng)掃清,下一篇我們就可以愉快地看FutureTask的源碼了(?ˉ?ˉ?)

(完)

查看更多系列文章: 系列文章目錄

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

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/77316.html

相關(guān)文章

  • FutureTask源碼解析(2)——深入理解FutureTask

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

    Harpsichord1207 評論0 收藏0
  • 系列文章目錄

    摘要:為了避免一篇文章的篇幅過長,于是一些比較大的主題就都分成幾篇來講了,這篇文章是筆者所有文章的目錄,將會持續(xù)更新,以給大家一個查看系列文章的入口。 前言 大家好,筆者是今年才開始寫博客的,寫作的初衷主要是想記錄和分享自己的學(xué)習(xí)經(jīng)歷。因為寫作的時候發(fā)現(xiàn),為了弄懂一個知識,不得不先去了解另外一些知識,這樣以來,為了說明一個問題,就要把一系列知識都了解一遍,寫出來的文章就特別長。 為了避免一篇...

    lijy91 評論0 收藏0
  • 系列文章目錄

    摘要:為了避免一篇文章的篇幅過長,于是一些比較大的主題就都分成幾篇來講了,這篇文章是筆者所有文章的目錄,將會持續(xù)更新,以給大家一個查看系列文章的入口。 前言 大家好,筆者是今年才開始寫博客的,寫作的初衷主要是想記錄和分享自己的學(xué)習(xí)經(jīng)歷。因為寫作的時候發(fā)現(xiàn),為了弄懂一個知識,不得不先去了解另外一些知識,這樣以來,為了說明一個問題,就要把一系列知識都了解一遍,寫出來的文章就特別長。 為了避免一篇...

    Yumenokanata 評論0 收藏0
  • 追蹤解析 FutureTask 源碼

    摘要:零前期準(zhǔn)備文章異常啰嗦且繞彎。版本版本簡介是中默認的實現(xiàn)類,常與結(jié)合進行多線程并發(fā)操作。所以方法的主體其實就是去喚醒被阻塞的線程。本文僅為個人的學(xué)習(xí)筆記,可能存在錯誤或者表述不清的地方,有緣補充 零 前期準(zhǔn)備 0 FBI WARNING 文章異常啰嗦且繞彎。 1 版本 JDK 版本 : OpenJDK 11.0.1 IDE : idea 2018.3 2 ThreadLocal 簡介 ...

    xcc3641 評論0 收藏0
  • jvm原理

    摘要:在之前,它是一個備受爭議的關(guān)鍵字,因為在程序中使用它往往收集器理解和原理分析簡稱,是后提供的面向大內(nèi)存區(qū)數(shù)到數(shù)多核系統(tǒng)的收集器,能夠?qū)崿F(xiàn)軟停頓目標(biāo)收集并且具有高吞吐量具有更可預(yù)測的停頓時間。 35 個 Java 代碼性能優(yōu)化總結(jié) 優(yōu)化代碼可以減小代碼的體積,提高代碼運行的效率。 從 JVM 內(nèi)存模型談線程安全 小白哥帶你打通任督二脈 Java使用讀寫鎖替代同步鎖 應(yīng)用情景 前一陣有個做...

    lufficc 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<