天天看點

Java并發程式設計:Callable、Future和FutureTask

版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。 https://blog.csdn.net/u010046908/article/details/56289187

1、簡介

在Java中建立線程的2種方式,一種是直接繼承Thread,另外一種就是實作Runnable接口。這2種方式都有一個缺陷就是:在執行完任務之後無法擷取執行結果。

2、需求

在Java中,如果需要擷取執行結果,就必須通過共享變量或者使用線程通信的方式來達到效果,這樣使用起來就比較麻煩。而自從Java 1.5開始,就提供了Callable和Future,通過它們可以在任務執行完畢之後得到任務執行結果。

3、Callable、FutureTask簡介

在學習Callable和FutureTask之前,我們先看一下java.lang.Runnable吧,它是一個接口,在它裡面隻聲明了一個run()方法:

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}           

由于run()方法傳回值為void類型,是以在執行完任務之後無法傳回任何結果。

Callable位于java.util.concurrent包下,它也是一個接口,在它裡面也隻聲明了一個方法,隻不過這個方法叫做call():

package java.util.concurrent;

/**
 * A task that returns a result and may throw an exception.
 * Implementors define a single method with no arguments called
 * {@code call}.
 *
 * <p>The {@code Callable} interface is similar to {@link
 * java.lang.Runnable}, in that both are designed for classes whose
 * instances are potentially executed by another thread.  A
 * {@code Runnable}, however, does not return a result and cannot
 * throw a checked exception.
 *
 * <p>The {@link Executors} class contains utility methods to
 * convert from other common forms to {@code Callable} classes.
 *
 * @see Executor
 * @since 1.5
 * @author Doug Lea
 * @param <V> the result type of method {@code call}
 */
@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;
}           

可以看到,這是一個泛型接口,call()函數傳回的類型就是傳遞進來的V類型。

那麼怎麼使用Callable呢?一般情況下是配合ExecutorService來使用的,在ExecutorService接口中聲明了若幹個submit方法的重載版本:

<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);           
  • 第一個submit方法裡面的參數類型就是Callable。
  • Callable一般是和ExecutorService配合來使用的,具體的使用方法講在後面講述。
  • 一般情況下我們使用第一個submit方法和第三個submit方法,第二個submit方法很少使用。
  • Future就是對于具體的Runnable或者Callable任務的執行結果進行取消、查詢是否完成、擷取結果。必要時可以通過get方法擷取執行結果,該方法會阻塞直到任務傳回結果。

Future類位于java.util.concurrent包下,它是一個接口:

public  interface  Future<V> {
     boolean  cancel( boolean  mayInterruptIfRunning);
     boolean  isCancelled();
     boolean  isDone();
     V get()  throws  InterruptedException, ExecutionException;
     V get( long  timeout, TimeUnit unit)
         throws  InterruptedException, ExecutionException, TimeoutException;
}           

在Future接口中聲明了5個方法,下面依次解釋每個方法的作用:

  • cancel方法用來取消任務,如果取消任務成功則傳回true,如果取消任務失敗則傳回false。參數mayInterruptIfRunning表示是否允許取消正在執行卻沒有執行完畢的任務,如果設定true,則表示可以取消正在執行過程中的任務。如果任務已經完成,則無論mayInterruptIfRunning為true還是false,此方法肯定傳回false,即如果取消已經完成的任務會傳回false;如果任務正在執行,若mayInterruptIfRunning設定為true,則傳回true,若mayInterruptIfRunning設定為false,則傳回false;如果任務還沒有執行,則無論mayInterruptIfRunning為true還是false,肯定傳回true。
  • isCancelled方法表示任務是否被取消成功,如果在任務正常完成前被取消成功,則傳回 true。
  • isDone方法表示任務是否已經完成,若任務完成,則傳回true;
  • get()方法用來擷取執行結果,這個方法會産生阻塞,會一直等到任務執行完畢才傳回;
  • get(long timeout, TimeUnit unit)用來擷取執行結果,如果在指定時間内,還沒擷取到結果,就直接傳回null。

Future提供了三種功能:

  1. 判斷任務是否完成;
  2. 能夠中斷任務;
  3. 能夠擷取任務執行結果。

因為Future隻是一個接口,是以是無法直接用來建立對象使用的,是以就有了下面的FutureTask。

4、Callable、FutureTask使用

4.1簡單使用

CallableTask.java

public class CallableTask implements Callable<Integer> {

        @Override
        public Integer call() throws Exception {
            int hours=5;
            int amount = 0;
            while(hours>0){
                System.out.println("我正在工作,剩餘時間 "+hours+"小時");
                amount++;
                hours--;
                Thread.sleep(1000);
            }
            return amount;
        }
}

           

TestDemo.java

public class TestDemo {

    public static void main(String args[]) throws ExecutionException {
        CallableTask worker = new CallableTask();
        FutureTask<Integer> jiangong = new FutureTask<Integer>(worker);
        new Thread(jiangong).start();

        while(!jiangong.isDone()){
            try {
                System.out.println("擷取結果"+jiangong.get());
                System.out.println("任務是否取消"+jiangong.isCancelled());
                System.out.println("任務是否執行"+jiangong.isDone());
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        int amount;
        try {
            amount = jiangong.get();
            System.out.println("工作做完了,上交了"+amount);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }


    }
}           

運作的結果:

4.2并發的實作

package com.lidong.demo;

import java.util.concurrent.Callable;

/**
 * @項目名稱:lidong-dubbo
 * @類名:Task
 * @類的描述:
 * @作者:lidong
 * @建立時間:2017/2/21 下午3:42
 * @公司:chni
 * @QQ:1561281670
 * @郵箱:[email protected]
 */
public class Task implements Callable<Integer> {



    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName()+"   子線程在進行計算開始");
        Thread.sleep(3000);
        int sum = 0;
        for(int i=0;i<100;i++)
            sum += i;
        System.out.println(Thread.currentThread().getName()+"   子線程在進行計算結束");
        return sum;
    }
}           

Test.java

package com.lidong.demo;

import java.util.concurrent.*;

public class Test {


    public static void main(String[] args) {

        ExecutorService executor = Executors.newCachedThreadPool();
        Task task = new Task();
        FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
        executor.submit(futureTask);
        executor.shutdown();

        System.out.println(Thread.currentThread().getName()+"  主線程在執行任務");

        try {
            System.out.println(Thread.currentThread().getName()+ "  task運作結果"+futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName()+  "  所有任務執行完畢");
    }
}
           

運作結果: