摘要:本文從源碼角度深刻解析Callable接口。
本文分享自華爲雲社區《深刻解析Callable接口》,做者: 冰 河 。java
本文純乾貨,從源碼角度深刻解析Callable接口,但願你們踏下心來,打開你的IDE,跟着文章看源碼,相信你必定收穫不小。緩存
1.Callable接口介紹
Callable接口是JDK1.5新增的泛型接口,在JDK1.8中,被聲明爲函數式接口,以下所示。安全
@FunctionalInterface public interface Callable<V> { V call() throws Exception; }
在JDK 1.8中只聲明有一個方法的接口爲函數式接口,函數式接口能夠使用@FunctionalInterface註解修飾,也能夠不使用@FunctionalInterface註解修飾。只要一個接口中只包含有一個方法,那麼,這個接口就是函數式接口。ide
在JDK中,實現Callable接口的子類以下圖所示。函數
默認的子類層級關係圖看不清,這裏,能夠經過IDEA右鍵Callable接口,選擇「Layout」來指定Callable接口的實現類圖的不一樣結構,以下所示。this
這裏,能夠選擇「Organic Layout」選項,選擇後的Callable接口的子類的結構以下圖所示。url
在實現Callable接口的子類中,有幾個比較重要的類,以下圖所示。spa
分別是:Executors類中的靜態內部類:PrivilegedCallable、PrivilegedCallableUsingCurrentClassLoader、RunnableAdapter和Task類下的TaskCallable。.net
2.實現Callable接口的重要類分析
接下來,分析的類主要有:PrivilegedCallable、PrivilegedCallableUsingCurrentClassLoader、RunnableAdapter和Task類下的TaskCallable。雖然這些類在實際工做中不多被直接用到,可是做爲一名合格的開發工程師,設置是禿頂的資深專家來講,瞭解並掌握這些類的實現有助你進一步理解Callable接口,並提升專業技能(頭髮再掉一批,哇哈哈哈。。。)。線程
- PrivilegedCallable
PrivilegedCallable類是Callable接口的一個特殊實現類,它代表Callable對象有某種特權來訪問系統的某種資源,PrivilegedCallable類的源代碼以下所示。
/** * A callable that runs under established access control settings */ static final class PrivilegedCallable<T> implements Callable<T> { private final Callable<T> task; private final AccessControlContext acc; PrivilegedCallable(Callable<T> task) { this.task = task; this.acc = AccessController.getContext(); } public T call() throws Exception { try { return AccessController.doPrivileged( new PrivilegedExceptionAction<T>() { public T run() throws Exception { return task.call(); } }, acc); } catch (PrivilegedActionException e) { throw e.getException(); } } }
從PrivilegedCallable類的源代碼來看,能夠將PrivilegedCallable當作是對Callable接口的封裝,而且這個類也繼承了Callable接口。
在PrivilegedCallable類中有兩個成員變量,分別是Callable接口的實例對象和AccessControlContext類的實例對象,以下所示。
private final Callable<T> task; private final AccessControlContext acc;
其中,AccessControlContext類能夠理解爲一個具備系統資源訪問決策的上下文類,經過這個類能夠訪問系統的特定資源。經過類的構造方法能夠看出,在實例化AccessControlContext類的對象時,只須要傳遞Callable接口子類的對象便可,以下所示。
PrivilegedCallable(Callable<T> task) { this.task = task; this.acc = AccessController.getContext(); }
AccessControlContext類的對象是經過AccessController類的getContext()方法獲取的,這裏,查看AccessController類的getContext()方法,以下所示。
public static AccessControlContext getContext(){ AccessControlContext acc = getStackAccessControlContext(); if (acc == null) { return new AccessControlContext(null, true); } else { return acc.optimize(); } }
經過AccessController的getContext()方法能夠看出,首先經過getStackAccessControlContext()方法來獲取AccessControlContext對象實例。若是獲取的AccessControlContext對象實例爲空,則經過調用AccessControlContext類的構造方法實例化,不然,調用AccessControlContext對象實例的optimize()方法返回AccessControlContext對象實例。
這裏,咱們先看下getStackAccessControlContext()方法是個什麼鬼。
private static native AccessControlContext getStackAccessControlContext();
原來是個本地方法,方法的字面意思就是獲取可以訪問系統棧的決策上下文對象。
接下來,咱們回到PrivilegedCallable類的call()方法,以下所示。
public T call() throws Exception { try { return AccessController.doPrivileged( new PrivilegedExceptionAction<T>() { public T run() throws Exception { return task.call(); } }, acc); } catch (PrivilegedActionException e) { throw e.getException(); } }
經過調用AccessController.doPrivileged()方法,傳遞PrivilegedExceptionAction。接口對象和AccessControlContext對象,並最終返回泛型的實例對象。
首先,看下AccessController.doPrivileged()方法,以下所示。
@CallerSensitive public static native <T> T doPrivileged(PrivilegedExceptionAction<T> action, AccessControlContext context) throws PrivilegedActionException;
能夠看到,又是一個本地方法。也就是說,最終的執行狀況是將PrivilegedExceptionAction接口對象和AccessControlContext對象實例傳遞給這個本地方法執行。而且在PrivilegedExceptionAction接口對象的run()方法中調用Callable接口的call()方法來執行最終的業務邏輯,而且返回泛型對象。
- PrivilegedCallableUsingCurrentClassLoader
此類表示爲在已經創建的特定訪問控制和當前的類加載器下運行的Callable類,源代碼以下所示。
/** * A callable that runs under established access control settings and * current ClassLoader */ static final class PrivilegedCallableUsingCurrentClassLoader<T> implements Callable<T> { private final Callable<T> task; private final AccessControlContext acc; private final ClassLoader ccl; PrivilegedCallableUsingCurrentClassLoader(Callable<T> task) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION); sm.checkPermission(new RuntimePermission("setContextClassLoader")); } this.task = task; this.acc = AccessController.getContext(); this.ccl = Thread.currentThread().getContextClassLoader(); } public T call() throws Exception { try { return AccessController.doPrivileged( new PrivilegedExceptionAction<T>() { public T run() throws Exception { Thread t = Thread.currentThread(); ClassLoader cl = t.getContextClassLoader(); if (ccl == cl) { return task.call(); } else { t.setContextClassLoader(ccl); try { return task.call(); } finally { t.setContextClassLoader(cl); } } } }, acc); } catch (PrivilegedActionException e) { throw e.getException(); } } }
這個類理解起來比較簡單,首先,在類中定義了三個成員變量,以下所示。
private final Callable<T> task; private final AccessControlContext acc; private final ClassLoader ccl;
接下來,經過構造方法注入Callable對象,在構造方法中,首先獲取系統安全管理器對象實例,經過系統安全管理器對象實例檢查是否具備獲取ClassLoader和設置ContextClassLoader的權限。並在構造方法中爲三個成員變量賦值,以下所示。
PrivilegedCallableUsingCurrentClassLoader(Callable<T> task) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION); sm.checkPermission(new RuntimePermission("setContextClassLoader")); } this.task = task; this.acc = AccessController.getContext(); this.ccl = Thread.currentThread().getContextClassLoader(); }
接下來,經過調用call()方法來執行具體的業務邏輯,以下所示。
public T call() throws Exception { try { return AccessController.doPrivileged( new PrivilegedExceptionAction<T>() { public T run() throws Exception { Thread t = Thread.currentThread(); ClassLoader cl = t.getContextClassLoader(); if (ccl == cl) { return task.call(); } else { t.setContextClassLoader(ccl); try { return task.call(); } finally { t.setContextClassLoader(cl); } } } }, acc); } catch (PrivilegedActionException e) { throw e.getException(); } }
在call()方法中一樣是經過調用AccessController類的本地方法doPrivileged,傳遞PrivilegedExceptionAction接口的實例對象和AccessControlContext類的對象實例。
具體執行邏輯爲:在PrivilegedExceptionAction對象的run()方法中獲取當前線程的ContextClassLoader對象,若是在構造方法中獲取的ClassLoader對象與此處的ContextClassLoader對象是同一個對象(不止對象實例相同,並且內存地址也相同),則直接調用Callable對象的call()方法返回結果。不然,將PrivilegedExceptionAction對象的run()方法中的當前線程的ContextClassLoader設置爲在構造方法中獲取的類加載器對象,接下來,再調用Callable對象的call()方法返回結果。最終將當前線程的ContextClassLoader重置爲以前的ContextClassLoader。
- RunnableAdapter
RunnableAdapter類比較簡單,給定運行的任務和結果,運行給定的任務並返回給定的結果,源代碼以下所示。
/** * A callable that runs given task and returns given result */ 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; } }
- TaskCallable
TaskCallable類是javafx.concurrent.Task類的靜態內部類,TaskCallable類主要是實現了Callable接口而且被定義爲FutureTask的類,而且在這個類中容許咱們攔截call()方法來更新task任務的狀態。源代碼以下所示。
private static final class TaskCallable<V> implements Callable<V> { private Task<V> task; private TaskCallable() { } @Override public V call() throws Exception { task.started = true; task.runLater(() -> { task.setState(State.SCHEDULED); task.setState(State.RUNNING); }); try { final V result = task.call(); if (!task.isCancelled()) { task.runLater(() -> { task.updateValue(result); task.setState(State.SUCCEEDED); }); return result; } else { return null; } } catch (final Throwable th) { task.runLater(() -> { task._setException(th); task.setState(State.FAILED); }); if (th instanceof Exception) { throw (Exception) th; } else { throw new Exception(th); } } } }
從TaskCallable類的源代碼能夠看出,只定義了一個Task類型的成員變量。下面主要分析TaskCallable類的call()方法。
當程序的執行進入到call()方法時,首先將task對象的started屬性設置爲true,表示任務已經開始,而且將任務的狀態依次設置爲State.SCHEDULED和State.RUNNING,依次觸發任務的調度事件和運行事件。以下所示。
task.started = true; task.runLater(() -> { task.setState(State.SCHEDULED); task.setState(State.RUNNING); });
接下來,在try代碼塊中執行Task對象的call()方法,返回泛型對象。若是任務沒有被取消,則更新任務的緩存,將調用call()方法返回的泛型對象綁定到Task對象中的ObjectProperty<V>對象中,其中,ObjectProperty<V>在Task類中的定義以下。
private final ObjectProperty<V> value = new SimpleObjectProperty<>(this, "value");
接下來,將任務的狀態設置爲成功狀態。以下所示。
try { final V result = task.call(); if (!task.isCancelled()) { task.runLater(() -> { task.updateValue(result); task.setState(State.SUCCEEDED); }); return result; } else { return null; } }
若是程序拋出了異常或者錯誤,會進入catch()代碼塊,設置Task對象的Exception信息並將狀態設置爲State.FAILED,也就是將任務標記爲失敗。接下來,判斷異常或錯誤的類型,若是是Exception類型的異常,則直接強轉爲Exception類型的異常並拋出。不然,將異常或者錯誤封裝爲Exception對象並拋出,以下所示。
catch (final Throwable th) { task.runLater(() -> { task._setException(th); task.setState(State.FAILED); }); if (th instanceof Exception) { throw (Exception) th; } else { throw new Exception(th); } }
好了,今天就到這兒吧,我是冰河,咱們下期見~~