天天看點

Java多線程 Callable Future FutureTask

1.Callable

Callable是類似于Runnable的接口,實作Callable接口的類和實作Runnable的類都是可被其它線程執行的任務。

Callable和Runnable有幾點不同:

 (1)Callable規定的方法是call(),而Runnable規定的方法是run().

 (2)Callable的任務執行後可傳回值,而Runnable的任務是不能傳回值的。

 (3)call()方法可抛出異常,而run()方法是不能抛出異常的。

 (4)運作Callable任務可拿到一個Future對象,

Future 表示異步計算的結果。它提供了檢查計算是否完成的方法,以等待計算的完成,并檢索計算的結果。

通過Future對象可了解任務執行情況,可取消任務的執行,還可擷取任務執行的結果。

例子:

public class CallableTest {

// 建立一個計算任務,傳回累加結果,構造器的參數是上界

static class SumCaller implements Callable<Long> {

private Integer count;

public SumCaller(Integer count) {

this.count = count;

}

public Long call() throws Exception {

long sum = 0;

for (int i = 0; i < count; i++) {

sum += i;

}

return sum;

}

}

private static Integer COUNT = 1000000000;

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

ExecutionException {

SumCaller caller = new SumCaller(COUNT);

FutureTask<Long> task = new FutureTask<Long>(caller);

Thread thread = new Thread(task);

thread.start();

long sum = task.get();

System.out.println("sum from 1 to " + COUNT + " result = " + sum);

}

}

2、Future

public interface Future<V> Future 表示異步計算的結果。

Future有個get方法而擷取結果隻有在計算完成時擷取,否則會一直阻塞直到任務轉入完成狀态,然後會傳回結果或者抛出異常。 

Future 主要定義了5個方法: 

1)boolean cancel(boolean mayInterruptIfRunning):試圖取消對此任務的執行。如果任務已完成、或已取消,或者由于某些其他原因而無法取消,則此嘗試将失敗。當調用 cancel 時,如果調用成功,而此任務尚未啟動,則此任務将永不運作。如果任務已經啟動,則 mayInterruptIfRunning 參數确定是否應該以試圖停止任務的方式來中斷執行此任務的線程。此方法傳回後,對 isDone() 的後續調用将始終傳回 true。如果此方法傳回 true,則對 isCancelled() 的後續調用将始終傳回 true。 

2)boolean isCancelled():如果在任務正常完成前将其取消,則傳回 true。 

3)boolean isDone():如果任務已完成,則傳回 true。 可能由于正常終止、異常或取消而完成,在所有這些情況中,此方法都将傳回 true。 

4)V get()throws InterruptedException,ExecutionException:如有必要,等待計算完成,然後擷取其結果。 

5)V get(long timeout,TimeUnit unit) throws InterruptedException,ExecutionException,TimeoutException:如有必要,最多等待為使計算完成所給定的時間之後,擷取其結果(如果結果可用)。

3、FutureTask

public class FutureTask  extends Object implements Future, Runnable

FutureTask類是Future 的一個實作,并實作了Runnable,是以可通過Excutor(線程池) 來執行,也可傳遞給Thread對象執行。如果在主線程中需要執行比較耗時的操作時,但又不想阻塞主線程時,可以把這些作業交給Future對象在背景完成,當主線程将來需要時,就可以通過Future對象獲得背景作業的計算結果或者執行狀态。 

Executor架構利用FutureTask來完成異步任務,并可以用來進行任何潛在的耗時的計算。一般FutureTask多用于耗時的計算,主線程可以在完成自己的任務後,再去擷取結果。

JDK:

此類提供了對 Future 的基本實作。僅在計算完成時才能檢索結果;如果計算尚未完成,則阻塞 get 方法。一旦計算完成,就不能再重新開始或取消計算。

可使用 FutureTask 包裝 Callable 或 Runnable 對象。因為 FutureTask 實作了 Runnable,是以可将 FutureTask 送出給 Executor 執行。

構造方法摘要

FutureTask(Callable<V> callable) 

          建立一個 FutureTask,一旦運作就執行給定的 Callable。

FutureTask(Runnable runnable, V result) 

          建立一個 FutureTask,一旦運作就執行給定的 Runnable,并安排成功完成時 get 傳回給定的結果 。

參數:

runnable - 可運作的任務。

result - 成功完成時要傳回的結果。

如果不需要特定的結果,則考慮使用下列形式的構造:Future<?> f = new FutureTask<Object>(runnable, null)

例子

常用的Thread類在run方法執行完之後是沒有傳回值的,要實作子線程完成任務後傳回值給主線程需要借助第三方轉存。Callable接口則提供了一種有傳回值的多線程實作方法。下面以一個簡單的地主、監工和長工的例子展示這種接口的用法。

長工類:

長工類實作了Callable接口,線程運作完成後傳回一個Integer值。 

import java.util.concurrent.Callable;

public class Changgong implements Callable<Integer>{

    private int hours=12;

    private int amount;

    @Override

    public Integer call() throws Exception {

        while(hours>0){

            System.out.println("I'm working......");

            amount ++;

            hours--;

            Thread.sleep(1000);

        }

        return amount;

    }

}

地主:主程序

監工:FutureTask 

import java.util.concurrent.ExecutionException;

import java.util.concurrent.FutureTask;

public class Dizhu {

    public static void main(String args[]){

        Changgong worker = new Changgong();

        FutureTask<Integer> jiangong = new FutureTask<Integer>(worker);

        new Thread(jiangong).start();

        while(!jiangong.isDone()){

            try {

                System.out.println("看長工做完了沒...");

                Thread.sleep(1000);

            } catch (InterruptedException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

        }

        int amount;

        try {

            amount = jiangong.get();

            System.out.println("工作做完了,上交了"+amount);

        } catch (InterruptedException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

        } catch (ExecutionException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

        }

    }

}

運作結果:

看勞工做完了沒...

I'm working......

看勞工做完了沒...

I'm working......

看勞工做完了沒...

I'm working......

看勞工做完了沒...

I'm working......

看勞工做完了沒...

I'm working......

看勞工做完了沒...

I'm working......

看勞工做完了沒...

I'm working......

看勞工做完了沒...

I'm working......

看勞工做完了沒...

I'm working......

看勞工做完了沒...

I'm working......

看勞工做完了沒...

I'm working......

看勞工做完了沒...

I'm working......

看勞工做完了沒...

工作做完了,上交了12

從上面的例子可以看出,FutureTask 扮演了一個監工的角色,地主(主程序)通過不斷地詢問監工(isDone()方法)可以得知長工的工作是否完成,并且在長工完成工作後讓監工收取成果(get()方法)。