欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

簡(jiǎn)談java并發(fā)FutureTask的實(shí)現(xiàn)

 更新時(shí)間:2019年06月14日 15:03:28   作者:宸小朔  
這篇文章主要介紹了簡(jiǎn)談java并發(fā)FutureTask的實(shí)現(xiàn),FutureTask都是用于獲取線程執(zhí)行的返回結(jié)果。文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,,需要的朋友可以參考下

概述

在使用java多線程解決問題的時(shí)候,為了提高效率,我們常常會(huì)異步處理一些計(jì)算任務(wù)并在最后異步的獲取計(jì)算結(jié)果,這個(gè)過程的實(shí)現(xiàn)離不開Future接口及其實(shí)現(xiàn)類FutureTask。FutureTask類實(shí)現(xiàn)了Runnable, Future接口,接下來(lái)我會(huì)通過源碼對(duì)該類的實(shí)現(xiàn)進(jìn)行詳解。

使用

我們先看下FutureTask中的主要方法如下,可以看出FutureTask實(shí)現(xiàn)了任務(wù)及異步結(jié)果的集合功能??吹竭@塊的方法,大家肯定會(huì)有疑問,Runnable任務(wù)的run方法返回空,F(xiàn)utureTask如何依靠該方法獲取線程異步執(zhí)行結(jié)果,這個(gè)問題,我們?cè)谙旅娼o大家介紹。

//以下五個(gè)方法實(shí)現(xiàn)接口Future中方法
public boolean isCancelled(); 
public boolean isDone(); 
public boolean cancel();
public V get() throws InterruptedException, ExecutionException;
public V get(long timeout, TimeUnit unit);
//實(shí)現(xiàn)接口Runnable中方法
public void run();

我們?cè)谑褂弥袝?huì)構(gòu)造一個(gè)FutureTask對(duì)象,然后將FutureTask扔到另一個(gè)線程中執(zhí)行,而主線程繼續(xù)執(zhí)行其他業(yè)務(wù)邏輯,一段時(shí)間后主線程調(diào)用FutureTask的get方法獲取執(zhí)行結(jié)果。下面我們看一個(gè)簡(jiǎn)單的例子:

/**
* Created by yuanqiongqiong on 2019/4/9.
*/
public class FutureTaskTest {
private static ExecutorService executorService = Executors.newFixedThreadPool(1);
public static void main(String []args) {
Callable callable = new AccCallable(1, 2);
FutureTask futureTask = new FutureTask(callable);
executorService.execute(futureTask);
System.out.println("go to do other things in main thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("go back in main thread");
try {
int result = (int) futureTask.get();
System.out.println("result is " + result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
static class AccCallable implements Callable<Integer> {
private int a;
private int b;
public AccCallable(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public Integer call() throws Exception {
System.out.println("acc a and b in threadId = " + Thread.currentThread().getName());
return a + b;
}
}
}

輸出結(jié)果為:

go to do other things in main thread
acc a and b in threadId = pool-1-thread-1
go back in main thread
result is 3

實(shí)現(xiàn)分析

在分析實(shí)現(xiàn)前,我們先想下如果讓我們實(shí)現(xiàn)一個(gè)類似FutureTask的功能,我們會(huì)如何做?因?yàn)樾枰@取執(zhí)行結(jié)果,需要一個(gè)Object對(duì)象來(lái)存執(zhí)行結(jié)果。任務(wù)執(zhí)行時(shí)間不可控性,我們需要一個(gè)變量表示執(zhí)行狀態(tài)。其他線程會(huì)調(diào)用get方法獲取結(jié)果,在沒達(dá)到超時(shí)的時(shí)候需要將線程阻塞或掛起。

因此需要一個(gè)隊(duì)列類似的結(jié)構(gòu)存儲(chǔ)等待該結(jié)果的線程信息,這樣在任務(wù)執(zhí)行線程完成后就可以喚醒這些阻塞或掛起的線程,得到結(jié)果。FutureTask的實(shí)際實(shí)現(xiàn)也是類似的邏輯,具體如下。

首先看下FutureTask的主要成員變量如下:

//futureTask執(zhí)行狀態(tài)
private volatile int state;
//具體的執(zhí)行任務(wù),會(huì)在run方法中抵用callable.call()
private Callable<V> callable;
//執(zhí)行結(jié)果
private Object outcome; 
//獲取結(jié)果的等待線程節(jié)點(diǎn)
private volatile WaitNode waiters;

對(duì)于執(zhí)行狀態(tài),在源碼中已經(jīng)有了非常清晰的解釋,這里我只是貼出源碼,不在進(jìn)行說明,具體如下:

/**
* Possible state transitions:
* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED
*/
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;

然后我們看下FutureTask的構(gòu)造函數(shù),如下:

public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
//構(gòu)造函數(shù)傳入runnable對(duì)象時(shí)調(diào)用靜態(tài)工具類Executors的方法轉(zhuǎn)換為一個(gè)callable對(duì)象
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}

如前所述,F(xiàn)utureTask的執(zhí)行線程中會(huì)調(diào)用其run()方法執(zhí)行任務(wù),我們看下這塊邏輯:

public void run() {
//1.如果執(zhí)行狀態(tài)不是NEW或者有其他線程執(zhí)行該任務(wù),直接返回
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
//2.如果執(zhí)行狀態(tài)是NEW,即任務(wù)還沒執(zhí)行,直接調(diào)用callable.call()方法獲取執(zhí)行結(jié)果
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
//3.發(fā)生異常,更新status為EXCEPTIONAL,喚醒掛起線程
setException(ex);
}
//4.如果結(jié)果成功返回,調(diào)用set方法將設(shè)置outcome,更改status執(zhí)行狀態(tài),喚醒掛起線程
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);
}
}

我們看下set函數(shù)的實(shí)現(xiàn),具體看下4中的執(zhí)行:

protected void set(V v) {
//將執(zhí)行狀態(tài)變更為COMPLETING
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
//設(shè)置執(zhí)行結(jié)果
outcome = v;
//設(shè)置執(zhí)行狀態(tài)為NORMAL
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
//執(zhí)行完成后處理操作,具體就是遍歷阻塞鏈表,刪除鏈表節(jié)點(diǎn),并喚醒每個(gè)節(jié)點(diǎn)關(guān)聯(lián)的線程
finishCompletion();
}
}

以上就是任務(wù)執(zhí)行線程做的邏輯,以上邏輯也回答了FutureTask如何得到執(zhí)行結(jié)果的疑問。下面我們看下用戶調(diào)用get方法獲取執(zhí)行結(jié)果時(shí)的實(shí)現(xiàn)邏輯,這個(gè)時(shí)候FutureTask可能處理各種狀態(tài),即可能沒有執(zhí)行,執(zhí)行中,已完成,發(fā)生異常等,具體如下:

public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
if (unit == null)
throw new NullPointerException();
int s = state;
//執(zhí)行狀態(tài)是NEW或者COMPLETING時(shí)執(zhí)行awaitDone將線程加入等待隊(duì)列中并掛起線程
if (s <= COMPLETING &&
(s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
throw new TimeoutException();
//根據(jù)執(zhí)行狀態(tài)status進(jìn)行結(jié)果封裝
return report(s);
}
//我理解這塊是get的核心邏輯
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
//如果設(shè)置了超時(shí)時(shí)間,計(jì)算還有多長(zhǎng)時(shí)間超時(shí)
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) {
//如果當(dāng)前線程被中斷,刪除等待隊(duì)列中的節(jié)點(diǎn),并拋出異常
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
int s = state;
//如果執(zhí)行狀態(tài)已經(jīng)完成或者發(fā)生異常,直接跳出自旋返回
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
//如果執(zhí)行狀態(tài)是正在執(zhí)行,說明線程已經(jīng)被加入到等待隊(duì)列中,放棄cpu進(jìn)入下次循環(huán)(真正的自旋)
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
//第一次進(jìn)入循環(huán),創(chuàng)建節(jié)點(diǎn)
else if (q == null)
q = new WaitNode();
//將節(jié)點(diǎn)加入到等待隊(duì)列中,waiters相當(dāng)于頭階段,不斷將頭結(jié)點(diǎn)更新為新節(jié)點(diǎn)
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) {
//如果設(shè)置了超時(shí)時(shí)間,在進(jìn)行下次循環(huán)前查看是否已經(jīng)超時(shí),如果超時(shí)刪除該節(jié)點(diǎn)進(jìn)行返回
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
//掛起當(dāng)前節(jié)點(diǎn)
LockSupport.parkNanos(this, nanos);
}
else
LockSupport.park(this);
}
}

這里需要說明一點(diǎn),F(xiàn)utureTask中的阻塞隊(duì)列新加入的節(jié)點(diǎn)都在頭結(jié)點(diǎn)并且next指向之前的頭結(jié)點(diǎn),waitars指針總是指向新加入節(jié)點(diǎn),通過waitars可以遍歷整個(gè)等待隊(duì)列,具體截圖如下。此外等待隊(duì)列節(jié)點(diǎn)結(jié)構(gòu)很簡(jiǎn)單成員變量只有線程引用和next指針,這里再列出器接口。

futureTask等待隊(duì)列

讀到這里,相信大家已經(jīng)對(duì)FutureTask的實(shí)現(xiàn)細(xì)節(jié)有了一定的認(rèn)識(shí)。此外,F(xiàn)utureTask沒有使用鎖而是使用Unsafe的是CAS的原子操作來(lái)解決競(jìng)爭(zhēng)問題,減少了鎖帶來(lái)的上下文切換的開銷,提高了效率。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • common-upload上傳文件功能封裝類分享

    common-upload上傳文件功能封裝類分享

    本文介紹一個(gè)common-upload上傳封裝類,為了更方便的上傳文件,對(duì)common-upload進(jìn)行了一個(gè)簡(jiǎn)單的封裝,大家參考使用吧
    2014-01-01
  • SpringBoot?快速實(shí)現(xiàn)?api?接口加解密功能

    SpringBoot?快速實(shí)現(xiàn)?api?接口加解密功能

    在項(xiàng)目中,為了保證數(shù)據(jù)的安全,我們常常會(huì)對(duì)傳遞的數(shù)據(jù)進(jìn)行加密,Spring?Boot接口加密,可以對(duì)返回值、參數(shù)值通過注解的方式自動(dòng)加解密,這篇文章主要介紹了SpringBoot?快速實(shí)現(xiàn)?api?接口加解密功能,感興趣的朋友一起看看吧
    2023-10-10
  • Java設(shè)計(jì)模式中觀察者模式詳解

    Java設(shè)計(jì)模式中觀察者模式詳解

    觀察者模式是極其重要的一個(gè)設(shè)計(jì)模式,也是我?guī)啄觊_發(fā)過程中使用最多的設(shè)計(jì)模式,本文首先概述觀察者模式的基本概念和Demo實(shí)現(xiàn),接著是觀察者模式在Java和Spring中的應(yīng)用,最后是對(duì)觀察者模式的應(yīng)用場(chǎng)景和優(yōu)缺點(diǎn)進(jìn)行總結(jié)
    2022-11-11
  • MyBatis實(shí)現(xiàn)SQL映射文件

    MyBatis實(shí)現(xiàn)SQL映射文件

    映射器是 MyBatis 中最復(fù)雜并且是最重要的組件,本文主要介紹了MyBatis實(shí)現(xiàn)SQL映射文件,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-06-06
  • Java Scanner如何獲取字符串和帶空格的字符串

    Java Scanner如何獲取字符串和帶空格的字符串

    這篇文章主要介紹了Java Scanner如何獲取字符串和帶空格的字符串問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • springboot開啟Bean數(shù)據(jù)校驗(yàn)功能

    springboot開啟Bean數(shù)據(jù)校驗(yàn)功能

    這篇文章主要介紹了springboot開啟Bean數(shù)據(jù)校驗(yàn)功能,通過啟用Bean屬性校驗(yàn)導(dǎo)入JSR303與Hibernate校驗(yàn)框架坐標(biāo),使用@Validated注解啟用校驗(yàn)功能,需要的朋友可以參考下
    2023-10-10
  • SpringCloud的@RefreshScope 注解你了解嗎

    SpringCloud的@RefreshScope 注解你了解嗎

    這篇文章主要介紹了Spring Cloud @RefreshScope 原理及使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-09-09
  • SpringMVC請(qǐng)求/響應(yīng)亂碼問題解決方案解析

    SpringMVC請(qǐng)求/響應(yīng)亂碼問題解決方案解析

    這篇文章主要介紹了SpringMVC請(qǐng)求/響應(yīng)亂碼問題解決方案解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • Spring Boot整合EasyExcel(完整版包含上傳解析excel和下載模板)

    Spring Boot整合EasyExcel(完整版包含上傳解析excel和下載模板)

    這篇文章主要介紹了Spring Boot整合EasyExcel(完整版包含上傳解析excel和下載模板),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • MyBatis直接執(zhí)行SQL的工具SqlMapper

    MyBatis直接執(zhí)行SQL的工具SqlMapper

    今天小編就為大家分享一篇關(guān)于MyBatis直接執(zhí)行SQL的工具SqlMapper,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-12-12

最新評(píng)論