JAVA並發編程:線程並發工具類Callable、Future 和FutureTask的使用

2021-03-02 菜鳥技術棧

1、基本介紹

  Runnable 是一個接口,在它裡面只聲明了一個 run()方法,由於 run()方法返回值為 void 類型,所以在執行完任務之後無法返回任何結果。
  Callable 位於 java.util.concurrent 包下,它也是一個接口,在它裡面也只聲明 了一個方法,只不過這個方法叫做 call(),這是一個泛型接口,call()函數返回的類型就是傳遞進來的 V 類型。
  Future 就是對於具體的 Runnable 或者 Callable 任務的執行結果進行取消、查詢是否完成、獲取結果。必要時可以通過 get 方法獲取執行結果,該方法會阻塞直到任務返回結果。在Future類中一共有5個方法,如下圖所示。

cancel(boolean mayInterruptIfRunning) 方法:取消任務,如果任務已經完成、已經被取消或由於某些其他原因而無法取消,則此嘗試將失敗。如果成功,並且在調用{@code cancel}時此任務尚未開始,則該任務永遠不會運行。如果任務已經開始,則{@code mayInterruptIfRunning}參數確定是否應中斷執行該任務的線程以嘗試停止該任務。

get() 方法:獲取返回結果

get(long timeout, TimeUnit unit) 方法:獲取結果時設置等待時長

isCancelled() 方法:如果此任務在正常完成之前被取消,則返回{@code true}。

isDone() 方法:如果此任務完成,則返回{@code true}。

  由於Future 只是一個接口,無法直接用來創建對象使用,因此就有了下面的FutureTask。FutureTask 類實現了RunnableFuture 接口,RunnableFuture 繼承了Runnable接口和Future 接口,而FutureTask 實現了RunnableFuture 接口。所以它既可以作為Runnable被線程執行,又可以作為Future 得到Callable 的返回值。FutureTask,可取消的異步計算。此類提供{@link Future}的基本實現,其中包含啟動和取消計算,查詢以查看計算是否完成以及檢索計算結果的方法。只有在計算完成後才能檢索結果;如果計算尚未完成,則{@code get}方法將阻塞。一旦計算完成,就不能重新開始或取消計算(除非使用{@link #runAndReset}調用計算)。UML類圖如下所示,可清晰看到幾個類的關係。

  因此我們通過一個線程運行Callable,但是Thread 不支持構造方法中傳遞Callable的實例,所以我們需要通過FutureTask 把一個Callable 包裝成Runnable,然後再通過這個FutureTask 拿到Callable 運行後的返回值。要new 一個FutureTask的實例,有兩種方法,如下圖所示。

2、代碼示例

package cn.lspj.ch2.future;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
* 類說明:Future的使用
*/
public class UseFuture {

/**
* 實現Callable接口,允許有返回值
*/
private static class UseCallable implements Callable<Integer> {

@Override
public Integer call() throws Exception {
System.out.println("Callable 子線程開始計算。。。。。。");
int num = 0;
for(int i=0;i<5000;i++){
num += i;
}
System.out.println("Callable 線程計算結果為 num =" + num);
return num;
}
}

public static void main(String[] args) throws ExecutionException, InterruptedException {

UseCallable useCallable = new UseCallable();
// 包裝
FutureTask futureTask = new FutureTask(useCallable);
new Thread(futureTask).start();
System.out.println("獲取子線程Callable計算返回結果:" + futureTask.get());
}

}

執行結果如下:

package cn.lspj.ch2.future;

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
* 類說明:Future的使用,演示在計算過程中中斷任務
*/
public class UseFuture {

/**
* 實現Callable接口,允許有返回值
*/
private static class UseCallable implements Callable<Integer> {

@Override
public Integer call() throws Exception {
System.out.println("Callable 子線程開始計算。。。。。。");
int num = 0;
for(int i=0;i<5000;i++){
if(Thread.currentThread().isInterrupted()){
System.out.println("Callable 子線程計算任務被中斷了。。。。。。。。。。");
return null;
}
Thread.sleep(1);
num += i;
System.out.println("num=" + num);
}
System.out.println("Callable 線程計算結果為 num =" + num);
return num;
}
}

public static void main(String[] args) throws ExecutionException, InterruptedException {

UseCallable useCallable = new UseCallable();
// 包裝
FutureTask futureTask = new FutureTask(useCallable);
new Thread(futureTask).start();
Thread.sleep(35);
Random r = new Random(10);
if(r.nextInt() > 5){
System.out.println("獲取子線程Callable計算返回結果:" + futureTask.get());
} else {
System.out.println("cancle....");
futureTask.cancel(true);
}
}

}

執行結果如下:

  從執行結果來看,在任務執行計算的過程中調用task的cancel(boolean mayInterruptIfRunning)方法中斷任務是可以達到中斷任務的目的,但是需要在執行計算的業務邏輯中使用Thread.currentThread().isInterrupted()判斷任務是否中斷,否則計算任務是不會主動中斷的。

更多精彩內容請關注「菜鳥技術棧」微信公眾號

CSDN:

https://blog.csdn.net/lspj201007186/article/details/106247283

往期內容:

JAVA並發編程:線程並發工具類CountDownLatch與CyclicBarrier的作用、應用場景和實戰

JAVA並發編程:線程並發工具類Fork-Join原理分析及實戰

算法:JAVA實現歸併排序

算法:插入排序

並發編程:線程中Join方法的使用示例分析

並發編程:線程基礎、JAVA新啟線程的方式

詳解Java 8 中使用Stream將List轉為Map

MYSQL之limit基本用法

相關焦點

  • 「原創」Java並發編程系列36|FutureTask
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫線程池源碼中出現了很多Callable、Future、FutureTask等以前沒介紹過的接口,尤其是線程池提交任務時總是把任務封裝成FutureTask,今天就來為大家解惑:
  • Java 並發編程之美-線程相關的基礎知識
    並發編程相比 Java 中其他知識點學習起來門檻相對較高,學習起來比較費勁,從而導致很多人望而卻步;而無論是職場面試和高並發高流量的系統的實現卻都還離不開並發編程,從而導致能夠真正掌握並發編程的人才成為市場比較迫切需求的。
  • Java並發編程Future超詳細教程
    Runnable VS Callable兩個接口都是用於多線程執行任務的,但他們還是有很明顯的差別的執行機制先從執行機制上來看,Runnable 你太清楚了,它既可以用在 Thread 類中,也可以用在 ExecutorService類中配合線程池的使用;Bu~~~~t, Callable 只能在 ExecutorService 中使用,你翻遍
  • Java多線程並發工具類-信號量Semaphore對象講解
    Java多線程並發工具類-Semaphore對象講解通過前面的學習,我們已經知道了Java多線程並發場景中使用比較多的兩個工具類:做加法的CycliBarrier對象以及做減法的CountDownLatch對象並對這兩個對象進行了比較。我們發現這兩個對象要麼是做加法,要麼是做減法的。那麼有沒有既做加法也做減法的呢?
  • Java多線程並發編程中並發容器第二篇之List的並發類講解
    Java多線程並發編程中並發容器第二篇之List的並發類講解概述本文我們將詳細講解list對應的並發容器以及用代碼來測試ArrayList、vector以及CopyOnWriteArrayList在100個線程向list中添加1000個數據後的比較
  • Java並發編程之支持並發的list集合你知道嗎
    Java並發編程之-list集合的並發.我們都知道Java集合類中的arrayList是線程不安全的。那麼怎麼證明是線程不安全的呢?怎麼解決在並發環境下使用安全的list集合類呢?本篇是《凱哥(凱哥Java:kagejava)並發編程學習》系列之《並發集合系列》教程的第一篇:本文主要內容:怎麼證明arrayList不是線程安全的?怎麼解決這個問題?以及遇到問題解決的四個步驟及從源碼來分析作者思路。一:怎麼證明arrayList在並發情況下是線程不安全的呢?
  • Python 3.8異步並發編程
    有效的提高程序執行效率的兩種方法是異步和並發,Golang,node.js之所以可以有很高執行效率主要是他們的協程和異步並發機制。實際上異步和並發是每一種現代語言都在追求的特性,當然Python也不例外,今天我們就講講Python 3中的異步並發編程。
  • Java8新的異步編程方式 CompletableFuture(一)
    Future接口是Java多線程Future模式的實現,在java.util.concurrent包中,可以來進行異步計算。Future模式是多線程設計常用的一種設計模式。Future模式可以理解成:我有一個任務,提交給了Future,Future替我完成這個任務。期間我自己可以去做任何想做的事情。一段時間之後,我就便可以從Future那兒取出結果。
  • Java是如何實現Future模式的?萬字詳解!
    :從以上代碼中可以看到,我們使用Future主要有以下步驟:新建一個Callable匿名函數實現類對象,我們的業務邏輯在Callable的call方法中實現,其中Callable的泛型是返回結果類型;然後把Callable匿名函數對象作為FutureTask的構造參數傳入,構建一個futureTask
  • java多線程——FutureTask的用法
    NEW -> CANCELLEDNEW -> INTERRUPTING -> INTERRUPTED其他變量:核心方法構造方法:FutureTask(Callable<V> callable
  • Java多線程帶返回值得Callable接口
    來看看這篇文章我們能學到什麼本節主要內容一:三種獲取多線程的的寫法二:分析第三種寫法的思想思路-使用了適配器模式三:第三種方法怎麼使用四:多個線程調用同一個futrueTask後,future的call
  • 「原創」Java並發編程系列33|深入理解線程池(上)
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫並發編程必不可少的線程池,接下來分兩篇文章介紹線程池,本文是第一篇。介紹1.1 使用場景並發編程可以高效利用CPU資源,提升任務執行效率,但是多線程及線程間的切換也伴隨著資源的消耗。當遇到單個任務處理時間比較短,但需要處理的任務數量很大時,線程會頻繁的創建銷毀,大量的時間和資源都會浪費在線程的創建和銷毀上,效率很低。
  • Java並發編程學習前期知識上篇
    Java並發編程-前期準備知識-上我們先來看看幾個大廠真實的面試題:從上面幾個真實的面試問題來看,我們可以看到大廠的面試都會問到並發相關的問題。所以Java並發,這個無論是面試還是在工作中,並發都是會遇到的。
  • 原創】Java並發編程系列01|開篇獲獎感言
    這個不寫並發程序的原則行得通的背景是那個時候基本都是單核處理器,系統並發量很低,藉助資料庫和類似Tomcat這種中間件就可以解決並發問題。如今硬體的驅動和網際網路行業的飛速發展,64核的處理器已經是很常見了,大型互聯廠商的系統並發量輕鬆過百萬,傳統的中間件和資料庫肯定是不能幫我們遮風避雨了,我們只能通過並發編程來解決這些問題。
  • Java中的並發工具類CountDownLatch
    Java中的並發工具類在多線程編程的時候,有時候需要控制並發流,Java本身提供了幾個控制並發的工具類,比如CountDownLatch,CyclicBarrier,Semaphore1、CountDownLatch允許一個或者多個線程等等其他線程完成。如果有個會議,等所有的人到了才能開始,假如每個人都是一個線程,開會需要等待每個線程結束。
  • Java 中這個叫 Future 的東東,你用過嘛?
    ,  Callable 只能在 ExecutorService 中使用,你翻遍 Thread 類,也找不到Callable 的身影我們也可以使用 Future 方法提供的 isDone 方法,它可以用來檢查 task 是否已經完成了,我們將上面程序做點小修改:// 如果子線程沒有結束,則睡眠 1s 重新檢查while(!futur
  • 【漫畫】面試經常問到的三個並發工具類
    如果你正準備找工作,那這篇文章就是為你量身準備的,不要想了,並發編程面試時一定會被問到的。
  • 「原創」Java並發編程系列02|並發編程三大核心問題
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫寫在前面編寫並發程序是比較困難的,因為並發程序極易出現Bug,這些Bug有都是比較詭異的,很多都是沒辦法追蹤,而且難以復現。
  • Java並發編程之驗證volatile的可見性
    Java並發編程之驗證volatile的可見性通過系列文章的學習,凱哥已經介紹了volatile的三大特性。1:保證可見性 2:不保證原子性 3:保證順序。那麼怎麼來驗證可見性呢?本文凱哥將通過代碼演示來證明volatile的可見性。
  • java並發編程-原子類
    原子類  原子操作是指不會被線程調度機制打斷的操作,這種操作一旦開始,就一直運行到結束,中間不會有任何線程上下文切換。而 java.util.concurrent.atomic 下的類,就是具有原子性的類,可以原子性地執行添加、遞增、遞減等操作。比如之前多線程下的線程不安全的 i++ 問題,到了原子類這裡,就可以用功能相同且線程安全的 getAndIncrement 方法來優雅地解決。