最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java實(shí)現(xiàn)FutureTask的示例詳解

 更新時間:2022年08月05日 10:20:30   作者:一無是處的研究僧  
在并發(fā)編程當(dāng)中我們最常見的需求就是啟動一個線程執(zhí)行一個函數(shù)去完成我們的需求,而在這種需求當(dāng)中,我們需要函數(shù)有返回值。Java給我們提供了這種機(jī)制,去實(shí)現(xiàn)這一個效果:FutureTask。本文為大家準(zhǔn)備了Java實(shí)現(xiàn)FutureTask的示例代碼,需要的可以參考一下

前言

在并發(fā)編程當(dāng)中我們最常見的需求就是啟動一個線程執(zhí)行一個函數(shù)去完成我們的需求,而在這種需求當(dāng)中,我們常常需要函數(shù)有返回值。比如我們需要同一個非常大的數(shù)組當(dāng)中數(shù)據(jù)的和,讓每一個線程求某一個區(qū)間內(nèi)部的和,最終將這些和加起來,那么每個線程都需要返回對應(yīng)區(qū)間的和。而在Java當(dāng)中給我們提供了這種機(jī)制,去實(shí)現(xiàn)這一個效果——FutureTask

FutureTask

在自己寫FutureTask之前我們首先寫一個例子來回顧一下FutureTask的編程步驟:

寫一個類實(shí)現(xiàn)Callable接口。

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

實(shí)現(xiàn)接口就實(shí)現(xiàn)call即可,可以看到這個函數(shù)是有返回值的,而FutureTask返回給我們的值就是這個函數(shù)的返回值。

new一個FutureTask對象,并且new一個第一步寫的類,new FutureTask<>(callable實(shí)現(xiàn)類)

最后將剛剛得到的FutureTask對象傳入Thread類當(dāng)中,然后啟動線程即可new Thread(futureTask).start();

然后我們可以調(diào)用FutureTaskget方法得到返回的結(jié)果futureTask.get();。

假如有一個數(shù)組data,長度為100000,現(xiàn)在有10個線程,第i個線程求數(shù)組[i * 10000, (i + 1) * 10000)所有數(shù)據(jù)的和,然后將這十個線程的結(jié)果加起來。

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
 
public class FutureTaskDemo {
 
  public static void main(String[] args) throws ExecutionException, InterruptedException {
    int[] data = new int[100000];
    Random random = new Random();
    for (int i = 0; i < 100000; i++) {
      data[i] = random.nextInt(10000);
    }
    @SuppressWarnings("unchecked")
    FutureTask<Integer>[] tasks = (FutureTask<Integer>[]) Array.newInstance(FutureTask.class, 10);
    // 設(shè)置10個 futuretask 任務(wù)計(jì)算數(shù)組當(dāng)中數(shù)據(jù)的和
    for (int i = 0; i < 10; i++) {
      int idx = i;
      tasks[i] = new FutureTask<>(() -> {
        int sum = 0;
        for (int k = idx * 10000; k < (idx + 1) * 10000; k++) {
          sum += data[k];
        }
        return sum;
      });
    }
    // 開啟線程執(zhí)行 futureTask 任務(wù)
    for (FutureTask<Integer> futureTask : tasks) {
      new Thread(futureTask).start();
    }
    int threadSum = 0;
    for (FutureTask<Integer> futureTask : tasks) {
      threadSum += futureTask.get();
    }
    int sum = Arrays.stream(data).sum();
    System.out.println(sum == threadSum); // 結(jié)果始終為 true
  }
}

可能你會對FutureTask的使用方式感覺困惑,或者不是很清楚,現(xiàn)在我們來仔細(xì)捋一下思路。

首先啟動一個線程要么是繼承自Thread類,然后重寫Thread類的run方法,要么是給Thread類傳遞一個實(shí)現(xiàn)了Runnable的類對象,當(dāng)然可以用匿名內(nèi)部類實(shí)現(xiàn)。

既然我們的FutureTask對象可以傳遞給Thread類,說明FutureTask肯定是實(shí)現(xiàn)了Runnable接口,我們現(xiàn)在來看一下FutureTask的繼承體系。

? 可以發(fā)現(xiàn)的是FutureTask確實(shí)實(shí)現(xiàn)了Runnable接口,同時還實(shí)現(xiàn)了Future接口,這個Future接口主要提供了后面我們使用FutureTask的一系列函數(shù)比如get

看到這里你應(yīng)該能夠大致想到在FutureTask中的run方法會調(diào)用Callable當(dāng)中實(shí)現(xiàn)的call方法,然后將結(jié)果保存下來,當(dāng)調(diào)用get方法的時候再將這個結(jié)果返回。

自己實(shí)現(xiàn)FutureTask

工具準(zhǔn)備

經(jīng)過上文的分析你可能已經(jīng)大致了解了FutureTask的大致執(zhí)行過程了,但是需要注意的是,如果你執(zhí)行FutureTaskget方法是可能阻塞的,因?yàn)榭赡?code>Callable的call方法還沒有執(zhí)行完成。因此在get方法當(dāng)中就需要有阻塞線程的代碼,但是當(dāng)call方法執(zhí)行完成之后需要將這些線程都喚醒。

在本篇文章當(dāng)中使用鎖ReentrantLock和條件變量Condition進(jìn)行線程的阻塞和喚醒,在我們自己動手實(shí)現(xiàn)FutureTask之前,我們先熟悉一下上面兩種工具的使用方法。

ReentrantLock主要有兩個方法:

  • lock對臨界區(qū)代碼塊進(jìn)行加鎖。
  • unlock對臨界區(qū)代碼進(jìn)行解鎖。

Condition主要有三個方法:

  • await阻塞調(diào)用這個方法的線程,等待其他線程喚醒。
  • signal喚醒一個被await方法阻塞的線程。
  • signalAll喚醒所有被await方法阻塞的線程。
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
 
public class LockDemo {
 
  private ReentrantLock lock;
  private Condition condition;
 
  LockDemo() {
    lock = new ReentrantLock();
    condition = lock.newCondition();
  }
 
  public void blocking() {
    lock.lock();
    try {
      System.out.println(Thread.currentThread() + " 準(zhǔn)備等待被其他線程喚醒");
      condition.await();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }finally {
      lock.unlock();
    }
  }
 
  public void inform() throws InterruptedException {
    // 先休眠兩秒 等他其他線程先阻塞
    TimeUnit.SECONDS.sleep(2);
    lock.lock();
    try {
      System.out.println(Thread.currentThread() + " 準(zhǔn)備喚醒其他線程");
      condition.signal(); // 喚醒一個被 await 方法阻塞的線程
      // condition.signalAll(); // 喚醒所有被 await 方法阻塞的線程
    }finally {
      lock.unlock();
    }
  }
 
  public static void main(String[] args) {
    LockDemo lockDemo = new LockDemo();
    Thread thread = new Thread(() -> {
      lockDemo.blocking(); // 執(zhí)行阻塞線程的代碼
    }, "Blocking-Thread");
    Thread thread1 = new Thread(() -> {
      try {
        lockDemo.inform(); // 執(zhí)行喚醒線程的代碼
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }, "Inform-Thread");
    thread.start();
    thread1.start();
  }
}

上面的代碼的輸出:

Thread[Blocking-Thread,5,main] 準(zhǔn)備等待被其他線程喚醒
Thread[Inform-Thread,5,main] 準(zhǔn)備喚醒其他線程

FutureTask設(shè)計(jì)與實(shí)現(xiàn)

在前文當(dāng)中我們已經(jīng)談到了FutureTask的實(shí)現(xiàn)原理,主要有以下幾點(diǎn):

  • 構(gòu)造函數(shù)需要傳入一個實(shí)現(xiàn)了Callable接口的類對象,這個將會在FutureTaskrun方法執(zhí)行,然后得到函數(shù)的返回值,并且將返回值存儲起來。
  • 當(dāng)線程調(diào)用get方法的時候,如果這個時候Callable當(dāng)中的call已經(jīng)執(zhí)行完成,直接返回call函數(shù)返回的結(jié)果就行,如果call函數(shù)還沒有執(zhí)行完成,那么就需要將調(diào)用get方法的線程掛起,這里我們可以使用condition.await()將線程掛起。
  • call函數(shù)執(zhí)行完成之后,需要將之前被get方法掛起的線程喚醒繼續(xù)執(zhí)行,這里使用condition.signalAll()將所有掛起的線程喚醒。
  • 因?yàn)槭俏覀冏约簩?shí)現(xiàn)FutureTask,功能不會那么齊全,只需要能夠滿足我們的主要需求即可,主要是幫助大家了解FutureTask原理。

實(shí)現(xiàn)代碼如下(分析都在注釋當(dāng)中):

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
 
// 這里需要實(shí)現(xiàn) Runnable 接口,因?yàn)樾枰獙⑦@個對象放入 Thread 類當(dāng)中
// 而 Thread 要求傳入的對象實(shí)現(xiàn)了 Runnable 接口
public class MyFutureTask<V> implements Runnable {
 
  private final Callable<V> callable;
  private Object returnVal; // 這個表示我們最終的返回值
  private final ReentrantLock lock;
  private final Condition condition;
 
  public MyFutureTask(Callable<V> callable) {
    // 將傳入的 callable 對象存儲起來 方便在后面的 run 方法當(dāng)中調(diào)用
    this.callable = callable;
    lock = new ReentrantLock();
    condition = lock.newCondition();
  }
 
  @SuppressWarnings("unchecked")
  public V get(long timeout, TimeUnit unit) {
    if (returnVal != null) // 如果符合條件 說明 call 函數(shù)已經(jīng)執(zhí)行完成 返回值已經(jīng)不為 null 了
      return (V) returnVal; // 直接將結(jié)果返回即可 這樣不用競爭鎖資源 提高程序執(zhí)行效率
    lock.lock();
    try {
      // 這里需要進(jìn)行二次判斷 (雙重檢查)
      // 因?yàn)槿绻粋€線程在第一次判斷 returnVal 為空
      // 然后這個時候它可能因?yàn)楂@取鎖而被掛起
      // 而在被掛起的這段時間,call 可能已經(jīng)執(zhí)行完成
      // 如果這個時候不進(jìn)行判斷直接執(zhí)行 await方法
      // 那后面這個線程將無法被喚醒
      if (returnVal == null)
        condition.await(timeout, unit);
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      lock.unlock();
    }
    return (V) returnVal;
  }
 
  @SuppressWarnings("unchecked")
  public V get() {
    if (returnVal != null)
      return (V) returnVal;
    lock.lock();
    try {
      // 同樣的需要進(jìn)行雙重檢查
      if (returnVal == null)
      	condition.await();
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      lock.unlock();
    }
    return (V) returnVal;
  }
 
 
  @Override
  public void run() {
    if (returnVal != null)
      return;
    try {
      // 在 Runnable 的 run 方法當(dāng)中
      // 執(zhí)行 Callable 方法的 call 得到返回結(jié)果
      returnVal = callable.call();
    } catch (Exception e) {
      e.printStackTrace();
    }
    lock.lock();
    try {
      // 因?yàn)橐呀?jīng)得到了結(jié)果
      // 因此需要將所有被 await 方法阻塞的線程喚醒
      // 讓他們從 get 方法返回
      condition.signalAll();
    }finally {
      lock.unlock();
    }
  }
	// 下面是測試代碼
  public static void main(String[] args) {
    MyFutureTask<Integer> ft = new MyFutureTask<>(() -> {
      TimeUnit.SECONDS.sleep(2);
      return 101;
    });
    Thread thread = new Thread(ft);
    thread.start();
    System.out.println(ft.get(100, TimeUnit.MILLISECONDS)); // 輸出為 null
    System.out.println(ft.get()); // 輸出為 101
  }
}

我們現(xiàn)在用我們自己寫的MyFutureTask去實(shí)現(xiàn)在前文當(dāng)中數(shù)組求和的例子:

public static void main(String[] args) throws ExecutionException, InterruptedException {
  int[] data = new int[100000];
  Random random = new Random();
  for (int i = 0; i < 100000; i++) {
    data[i] = random.nextInt(10000);
  }
  @SuppressWarnings("unchecked")
  MyFutureTask<Integer>[] tasks = (MyFutureTask<Integer>[]) Array.newInstance(MyFutureTask.class, 10);
  for (int i = 0; i < 10; i++) {
    int idx = i;
    tasks[i] = new MyFutureTask<>(() -> {
      int sum = 0;
      for (int k = idx * 10000; k < (idx + 1) * 10000; k++) {
        sum += data[k];
      }
      return sum;
    });
  }
  for (MyFutureTask<Integer> MyFutureTask : tasks) {
    new Thread(MyFutureTask).start();
  }
  int threadSum = 0;
  for (MyFutureTask<Integer> MyFutureTask : tasks) {
    threadSum += MyFutureTask.get();
  }
  int sum = Arrays.stream(data).sum();
  System.out.println(sum == threadSum); // 輸出結(jié)果為 true
}

總結(jié)

在本篇文章當(dāng)中主要給大家介紹了FutureTask的內(nèi)部原理,并且我們自己通過使用ReentrantLockCondition實(shí)現(xiàn)了我們自己的FutureTask,本篇文章的主要內(nèi)容如下:

FutureTask的內(nèi)部原理:

  • FutureTask首先會繼承Runnable接口,這樣就可以將FutureTask的對象直接放入Thread類當(dāng)中,作為構(gòu)造函數(shù)的參數(shù)。
  • 我們在使用FutureTask的時候需要傳入一個Callable實(shí)現(xiàn)類的對象,在函數(shù)call當(dāng)中實(shí)現(xiàn)我們需要執(zhí)行的函數(shù),執(zhí)行完成之后,將call函數(shù)的返回值保存下來,當(dāng)有線程調(diào)用get方法時候?qū)⒈4娴姆祷刂捣祷亍?/li>

我們使用條件變量進(jìn)行對線程的阻塞和喚醒。

  • 當(dāng)有線程調(diào)用get方法時,如果call已經(jīng)執(zhí)行完成,那么可以直接將結(jié)果返回,否則需要使用條件變量將線程掛起。
  • 當(dāng)call函數(shù)執(zhí)行完成的時候,需要使用條件變量將所有阻塞在get方法的線程喚醒。

雙重檢查:

  • 我們在get方法當(dāng)中首先判斷returnVal是否為空,如果不為空直接將結(jié)果返回,這就可以不用去競爭鎖資源了,可以提高程序執(zhí)行的效率。
  • 但是我們在使用鎖保護(hù)的臨界區(qū)還需要進(jìn)行判斷,判斷returnVal是否為空,因?yàn)槿绻粋€線程在第一次判斷 returnVal 為空,然后這個時候它可能因?yàn)楂@取鎖而被掛起, 而在被掛起的這段時間,call 可能已經(jīng)執(zhí)行完成,如果這個時候不進(jìn)行判斷直接執(zhí)行 await方法,那后面這個線程將無法被喚醒,因?yàn)樵?code>call函數(shù)執(zhí)行完成之后調(diào)用了condition.signalAll(),如果線程在這之后執(zhí)行await方法,那么將來再沒有線程去將這些線程喚醒。

到此這篇關(guān)于Java實(shí)現(xiàn)FutureTask的示例詳解的文章就介紹到這了,更多相關(guān)Java FutureTask內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java如何實(shí)時動態(tài)獲取properties文件的內(nèi)容

    java如何實(shí)時動態(tài)獲取properties文件的內(nèi)容

    這篇文章主要介紹了java如何實(shí)時動態(tài)獲取properties文件的內(nèi)容,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • MyBatis中example.createCriteria()方法的具體使用

    MyBatis中example.createCriteria()方法的具體使用

    本文詳細(xì)介紹了MyBatis的Example工具的使用方法,包括鏈?zhǔn)秸{(diào)用指定字段、設(shè)置查詢條件、支持多種查詢方式等,還介紹了mapper的crud方法、and/or方法的使用,以及如何進(jìn)行多條件和多重條件查詢,感興趣的可以了解一下
    2024-10-10
  • SpringBoot實(shí)現(xiàn)過濾器、攔截器與切片的實(shí)現(xiàn)和區(qū)別

    SpringBoot實(shí)現(xiàn)過濾器、攔截器與切片的實(shí)現(xiàn)和區(qū)別

    本文詳細(xì)介紹了使用過濾器、攔截器與切片實(shí)現(xiàn)每個請求耗時的統(tǒng)計(jì),并比較三者的區(qū)別與聯(lián)系,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-02-02
  • java實(shí)現(xiàn)注冊登錄系統(tǒng)

    java實(shí)現(xiàn)注冊登錄系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)注冊登錄系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • SpringCache輕松啟用Redis緩存的全過程

    SpringCache輕松啟用Redis緩存的全過程

    Spring Cache是Spring提供的一種緩存抽象機(jī)制,旨在通過簡化緩存操作來提高系統(tǒng)性能和響應(yīng)速度,本文將給大家詳細(xì)介紹SpringCache如何輕松啟用Redis緩存,文中有詳細(xì)的代碼示例供大家參考,需要的朋友可以參考下
    2024-07-07
  • Java實(shí)現(xiàn)發(fā)送郵件功能時碰到的坑

    Java實(shí)現(xiàn)發(fā)送郵件功能時碰到的坑

    之前用163郵箱發(fā)郵件時明明是成功的,但是使用中國移動自己的郵箱時,無論如何在linux服務(wù)器中都發(fā)送不成功。下面小編給大家說下我是怎么解決的,一起看下吧
    2016-06-06
  • Java中生成微信小程序太陽碼的實(shí)現(xiàn)方案

    Java中生成微信小程序太陽碼的實(shí)現(xiàn)方案

    這篇文章主要介紹了Java中生成微信小程序太陽碼的實(shí)現(xiàn)方案,本文講解了如何生成微信小程序太陽碼,通過微信提供的兩種方案都可以實(shí)現(xiàn),在實(shí)際的項(xiàng)目中建議采用第二種方案,需要的朋友可以參考下
    2022-05-05
  • Java集合中的List超詳細(xì)講解

    Java集合中的List超詳細(xì)講解

    本文詳細(xì)介紹了Java集合框架中的List接口,包括其在集合中的位置、繼承體系、常用操作和代碼示例,以及不同實(shí)現(xiàn)類(如ArrayList、LinkedList和Vector)的底層原理和應(yīng)用場景,感興趣的朋友一起看看吧
    2025-02-02
  • java實(shí)現(xiàn)撲克牌分發(fā)功能

    java實(shí)現(xiàn)撲克牌分發(fā)功能

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)撲克牌分發(fā),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • java中每月等額與先息后本計(jì)算

    java中每月等額與先息后本計(jì)算

    一般信用貸款會提供兩種還款方式:每月等額或者先息后本。每月等額,就是每月歸還等同的部分本金和利息,你手里在使用的本金其實(shí)是逐月減少的。先息后本就是先還利息,到期歸還本金。本文將介紹他們的實(shí)現(xiàn)方法。具有很好的參考價(jià)值,下面跟著小編一起來看下吧
    2017-03-03

最新評論

秦安县| 黄石市| 上高县| 石屏县| 大港区| 彰化县| 浙江省| 仲巴县| 邹平县| 额济纳旗| 常宁市| 江孜县| 乌鲁木齐县| 玛曲县| 凉城县| 兴城市| 卢湾区| 突泉县| 长海县| 芦山县| 临沭县| 郸城县| 湖口县| 绍兴县| 娱乐| 昭苏县| 陆川县| 微山县| 桑植县| 化德县| 沅江市| 南皮县| 秀山| 大庆市| 和龙市| 平安县| 邳州市| 罗江县| 桃园市| 榆社县| 武汉市|