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

Java中Future和FutureTask的示例詳解及使用

 更新時(shí)間:2021年11月30日 11:23:20   作者:Charte  
Java中的Future和FutureTask通常和線程池搭配使用,用來(lái)獲取線程池返回執(zhí)行后的返回值,下面這篇文章主要給大家介紹了關(guān)于Java中Future和FutureTask使用的相關(guān)資料,需要的朋友可以參考下

一、Future 接口

當(dāng) call()方法完成時(shí),結(jié)果必須存儲(chǔ)在主線程已知的對(duì)象中,以便主線程可以知道該線程返回的結(jié)果。為此,可以使用 Future 對(duì)象。

將 Future 視為保存結(jié)果的對(duì)象–它可能暫時(shí)不保存結(jié)果,但將來(lái)會(huì)保存(一旦Callable 返回)。Future 基本上是主線程可以跟蹤進(jìn)度以及其他線程的結(jié)果的一種方式。要實(shí)現(xiàn)此接口,必須重寫 5 種方法,這里列出了重要的方法,如下:

public boolean isDone()

public boolean cancel(boolean mayInterruptIfRunning)

用于停止任務(wù)。如果尚未啟動(dòng),它將停止任務(wù)。如果已啟動(dòng),則僅在 mayInterrupt 為 true時(shí)才會(huì)中斷任務(wù)。

boolean isCancelled()

如果任務(wù)在正常結(jié)束之前被取消返回true

public V get() throws InterruptedException, ExecutionException

用于獲取任務(wù)的結(jié)果。如果任務(wù)完成,它將立即返回結(jié)果,否則將等待任務(wù)完成,然后返回結(jié)果。

public V get(long timeout, TimeUnit unit)
    throws InterruptedException, ExecutionException, TimeoutException

如果任務(wù)完成,則返回 true,否則返回 false。

Callable 與 Runnable 類似,因?yàn)樗庋b了要在另一個(gè)線程上運(yùn)行的任務(wù),而 Future 用于存儲(chǔ)從另一個(gè)線程獲得的結(jié)果

實(shí)際上,F(xiàn)uture 也可以與 Runnable 一起使用。要?jiǎng)?chuàng)建線程,需要 Runnable。為了獲得結(jié)果,需要 future。

二、FutureTask

介紹:當(dāng)一個(gè)線程需要等待另一個(gè)線程把某個(gè)任務(wù)執(zhí)行完后它才能繼續(xù)執(zhí)行,此時(shí)可以使用FutureTask。假設(shè)有多個(gè)線程執(zhí)行若干任務(wù),每個(gè)任務(wù)最多只能被執(zhí)行一次。當(dāng)多個(gè)線程試圖同時(shí)執(zhí)行同一個(gè)任務(wù)時(shí),只允許一個(gè)線程執(zhí)行任務(wù),其他線程需要等待這個(gè)任務(wù)執(zhí)行完后才能繼續(xù)執(zhí)行。

Java 庫(kù)具有具體的 FutureTask 類型,該類型實(shí)現(xiàn) Runnable 和 Future,并方便地將兩種功能組合在一起。 可以通過(guò)為其構(gòu)造函數(shù)提供 Callable 來(lái)創(chuàng)建FutureTask。然后,將 FutureTask 對(duì)象提供給 Thread 的構(gòu)造函數(shù)以創(chuàng)建Thread 對(duì)象。因此,間接地使用 Callable 創(chuàng)建線程。

FutureTask狀態(tài)轉(zhuǎn)換

FutureTask有以下7種狀態(tài):

FutureTask任務(wù)的運(yùn)行狀態(tài),最初為NEW。運(yùn)行狀態(tài)僅在set、setException和cancel方法中轉(zhuǎn)換為終端狀態(tài)。在完成過(guò)程中,狀態(tài)可能呈現(xiàn)出瞬時(shí)值INTERRUPTING(僅在中斷運(yùn)行程序以滿足**cancel(true)**的情況下)或者COMPLETING(在設(shè)置結(jié)果時(shí))狀態(tài)時(shí)。從這些中間狀態(tài)到最終狀態(tài)的轉(zhuǎn)換使用成本更低的有序/延遲寫,因?yàn)橹凳墙y(tǒng)一的,需要進(jìn)一步修改。

state:表示當(dāng)前任務(wù)的運(yùn)行狀態(tài),F(xiàn)utureTask的所有方法都是圍繞state開展的,state聲明為volatile,保證了state的可見性,當(dāng)對(duì)state進(jìn)行修改時(shí)所有的線程都會(huì)看到。

NEW:表示一個(gè)新的任務(wù),初始狀態(tài)

COMPLETING:當(dāng)任務(wù)被設(shè)置結(jié)果時(shí),處于COMPLETING狀態(tài),這是一個(gè)中間狀態(tài)。

NORMAL:表示任務(wù)正常結(jié)束。

EXCEPTIONAL:表示任務(wù)因異常而結(jié)束

CANCELLED:任務(wù)還未執(zhí)行之前就調(diào)用了cancel(true)方法,任務(wù)處于CANCELLED

INTERRUPTING:當(dāng)任務(wù)調(diào)用cancel(true)中斷程序時(shí),任務(wù)處于INTERRUPTING狀態(tài),這是一個(gè)中間狀態(tài)。

INTERRUPTED:任務(wù)調(diào)用cancel(true)中斷程序時(shí)會(huì)調(diào)用interrupt()方法中斷線程運(yùn)行,任務(wù)狀態(tài)由INTERRUPTING轉(zhuǎn)變?yōu)镮NTERRUPTED

可能的狀態(tài)過(guò)渡:
1、NEW -> COMPLETING -> NORMAL:正常結(jié)束
2、NEW -> COMPLETING -> EXCEPTIONAL:異常結(jié)束
3、NEW -> CANCELLED:任務(wù)被取消
4、NEW -> INTERRUPTING -> INTERRUPTED:任務(wù)出現(xiàn)中斷

三、使用 Callable 和 Future

Runnable缺少的一項(xiàng)功能是,當(dāng)線程終止時(shí)(即 run()完成時(shí)),我們無(wú)法使線程返回結(jié)果。

為了支持此功能,Java 中提供了 Callable 接口。不能直接替換 runnable,因?yàn)?Thread 類的構(gòu)造方法根本沒(méi)有 Callable。

所以我們可以找一個(gè)中間人,也就是FutureTask。

案例

class MyThreadA implements Callable {
    @Override
    public Object call() throws Exception {
        System.out.println(Thread.currentThread().getName() + "在call方法里");
        System.out.println(Thread.currentThread().getName() + "線程進(jìn)入了 call方法,開始睡覺(進(jìn)行了一些計(jì)算)");
        Thread.sleep(10000);
        System.out.println(Thread.currentThread().getName() + "睡醒了");
        return Thread.currentThread().getName() + "返回的:" + System.currentTimeMillis();
    }
}


public class demo1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<Integer> futureTaskA = new FutureTask<>(new MyThreadA());

        FutureTask<String> futureTaskB = new FutureTask<>(()->{
            System.out.println(Thread.currentThread().getName() + "在call方法里");
            return Thread.currentThread().getName() + "返回的:" + System.currentTimeMillis();
        });

        new Thread(futureTaskA,"線程A").start();
        new Thread(futureTaskB,"線程B").start();

        while (!futureTaskB.isDone()){  //isDone表示FutureTask的計(jì)算是否完成
            System.out.println("wait.......");
        }
        System.out.println(futureTaskA.get());
        System.out.println(futureTaskB.get());

        System.out.println(Thread.currentThread().getName() + "結(jié)束了");
    }
}

輸出結(jié)果:

由上圖兩個(gè)線程返回的時(shí)間差約等于10秒可以看出,當(dāng)一個(gè)線程(線程B)需要等待(一直wait…)另一個(gè)線程(線程A)把某個(gè)任務(wù)(進(jìn)行了一些計(jì)算)執(zhí)行完后它才能繼續(xù)執(zhí)行,此時(shí)可以使用FutureTask。不管futureTaskA.get()和futureTaskB.get()誰(shuí)在前面,輸出結(jié)果一定是“線程B返回的:xxx”在“wait…”的后面。假設(shè)有多個(gè)線程執(zhí)行若干任務(wù),每個(gè)任務(wù)最多只能被執(zhí)行一次。當(dāng)多個(gè)線程試圖同時(shí)執(zhí)行同一個(gè)任務(wù)時(shí),只允許一個(gè)線程執(zhí)行任務(wù),其他線程需要等待這個(gè)任務(wù)執(zhí)行完后才能繼續(xù)執(zhí)行。

四、小結(jié)(FutureTask核心原理)

FutureTask核心原理

在主線程中需要執(zhí)行比較耗時(shí)的操作時(shí),但又不想阻塞主線程時(shí),可以把這些作業(yè)交給 Future 對(duì)象在后臺(tái)完成,當(dāng)主線程將來(lái)需要時(shí),就可以通過(guò) Future對(duì)象獲得后臺(tái)作業(yè)的計(jì)算結(jié)果或者執(zhí)行狀態(tài)。

? 一般 FutureTask 多用于耗時(shí)的計(jì)算,主線程可以在完成自己的任務(wù)后,再去獲取結(jié)果

? 僅在計(jì)算完成時(shí)才能檢索結(jié)果;如果計(jì)算尚未完成,則阻塞 get 方法。一旦計(jì)算完成,就不能再重新開始或取消計(jì)算。get 方法而獲取結(jié)果只有在計(jì)算完成時(shí)獲取,否則會(huì)一直阻塞直到任務(wù)轉(zhuǎn)入完成狀態(tài),然后會(huì)返回結(jié)果或者拋出異常。

? get只計(jì)算一次,因此 get 方法放到最后

附:FutureTask在高并發(fā)環(huán)境下確保任務(wù)只執(zhí)行一次

網(wǎng)上有篇例子,但是中間講的不是很清楚。我重新梳理了一下。

在很多高并發(fā)的環(huán)境下,往往我們只需要某些任務(wù)只執(zhí)行一次。這種使用情景FutureTask的特性恰能勝任。舉一個(gè)例子,假設(shè)有一個(gè)帶key的連接池,當(dāng)key存在時(shí),即直接返回key對(duì)應(yīng)的對(duì)象;當(dāng)key不存在時(shí),則創(chuàng)建連接。對(duì)于這樣的應(yīng)用場(chǎng)景,通常采用的方法為使用一個(gè)Map對(duì)象來(lái)存儲(chǔ)key和連接池對(duì)應(yīng)的對(duì)應(yīng)關(guān)系,典型的代碼如下面所示:

package com.concurrency.chapter15;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @program: 錯(cuò)誤示例
 *
 * @description: 在很多高并發(fā)的環(huán)境下,往往我們只需要某些任務(wù)只執(zhí)行一次。
 * 這種使用情景FutureTask的特性恰能勝任。舉一個(gè)例子,假設(shè)有一個(gè)帶key的連接池,
 * 當(dāng)key存在時(shí),即直接返回key對(duì)應(yīng)的對(duì)象;當(dāng)key不存在時(shí),則創(chuàng)建連接。對(duì)于這樣的應(yīng)用場(chǎng)景,
 * 通常采用的方法為使用一個(gè)Map對(duì)象來(lái)存儲(chǔ)key和連接池對(duì)應(yīng)的對(duì)應(yīng)關(guān)系,典型的代碼如下
 * 在例子中,我們通過(guò)加鎖確保高并發(fā)環(huán)境下的線程安全,也確保了connection只創(chuàng)建一次,然而卻犧牲了性能。
 *
 * @author: zhouzhixiang
 *
 * @create: 2019-05-14 20:22
 */
public class FutureTaskConnection1 {

    private static Map<String, Connection> connectionPool = new HashMap<>();
    private static ReentrantLock lock = new ReentrantLock();

    public static Connection getConnection(String key) {
        try {
            lock.lock();
            Connection connection = connectionPool.get(key);
            if (connection == null) {
                Connection newConnection = createConnection();
                connectionPool.put(key, newConnection);
                return newConnection;
            }
            return connection;
        } finally {
            lock.unlock();
        }
    }

    private static Connection createConnection() {
        try {
            return DriverManager.getConnection("");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }
}

總結(jié)

到此這篇關(guān)于Java中Future和FutureTask的文章就介紹到這了,更多相關(guān)Java?Future和FutureTask使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論