FutureとFutureTaskは非同期計算を実現


Androidアプリの開発では、AsyncTaskフレームワークを使用してリソースを非同期でロードしたり、サーバに非同期でメッセージを送信したりすることがよくあります.タスクが完了した後、UIマスタースレッドに結果をアクティブに更新すると、AsyncTaskフレームワークはスレッドの非同期実行結果を容易に取得できます.Java 5以前、JavaはAPIを提供していなかった.Java 5以降のバージョンで提供されるコンカレントフレームワークパッケージjava.util.concurrentは、マルチスレッドに対してより良いサポートを提供しています.FutureインタフェースとFutureTaskクラスは、非同期でタスクを実行するフレームワークの重要な構成部分です.より明確な理解のために、Runnable、Callable、ExecutorServiceなどのインタフェースから説明する必要があります.
Runnableインタフェース:instanceはnew Thread(Runnable r)を使用して新しいスレッドに入れて走ることができ、結果を返さない.また、ExecutorServices.submit(Runnable r)を使用してスレッドプールに配置することもできます.結果はnullで、結果は返されませんが、返されたFutureオブジェクトから実行ステータスを問い合わせることができます.
public abstract void run();

Callableインタフェース:instanceはExecutorServiceのスレッドプールでしか走ることができませんが、返された結果がある場合は、返されたFutureオブジェクトでステータスを問い合わせることもできます.表面的にはCallableインタフェースを戻り結果のあるRunnalbeインタフェースと簡単に理解できる.
V call() throws Exception;

ExecutorServiceインタフェース:スレッドプール実行スケジューラ
 Future submit(Callable task);

 Future submit(Runnable task, T result);

Future> submit(Runnable task);

Futureインタフェース:タスクの実行ステータスを照会したり、実行結果を取得したり、未実行のタスクをキャンセルしたりします.ExecutorServiceフレームワークでは、スレッドプールを使用するため、RunnableインスタンスとCallableインスタンスは「スレッド」ではなくタスクとして扱われるため、Futureではタスク実行のキャンセルなどのインタフェースがあります.インタフェースのget()メソッドは、タスクの実行結果を取得するために使用されます.タスクは非同期で実行されるため、結果を使用する必要がある場合にget()メソッドを呼び出すことができます.呼び出し時にタスクがまだ実行されていない場合、タスクが完了するまでブロックされます.もちろん、getの別のリロードバージョンget(long timeout,TimeUnit unit)を呼び出すこともできます.ブロックされると指定された時間を待つことができます.時間が来てもタスクが完了していない場合は、TimeoutExceptionを放出します.
    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

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

FutureTaskクラス:Runnable、Callable、Futureを一体化し、まずRunnableとFutureインタフェースを実現し、次にコンストラクション関数にCallableオブジェクト(または変形したCallableオブジェクト:Runnable+Result)を注入するので、FutureTaskクラスはnew Thread(Runnable r)を使用して新しいスレッドに配置したり、ExecutorServices.submit(Runnable)を使用したりすることができます.スレッドプールに移動し、両方の方法で戻り結果を得ることができますが、実質的には同じです.つまり、戻り結果がある場合は、コンストラクション関数は必ずCallableオブジェクトに注入するか、Runnableオブジェクトに予め与えられた結果を注入するか(個人的にはあまり役に立たないと思います).
public interface RunnableFuture extends Runnable, Future{
...}

public class FutureTask implements RunnableFuture {
public FutureTask(Callable callable)
public FutureTask(Runnable runnable, V result)
...
}

サンプルコード:
package com.stevex.app.forkjoin;

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

public class FutureTaskTest {

	public static void main(String[] args) {
		Callable c = new Callable() {
			public String call() {
				try {
					TimeUnit.SECONDS.sleep(new Random().nextInt(5));
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

				return "Callable--"+Thread.currentThread().getName();
			}
		};

		//seed a single thread
		FutureTask ft1 = new FutureTask(c);
		Thread t = new Thread(ft1);
		t.start();

		Runnable r = new Runnable() {
			public void run() {
				try {
					TimeUnit.SECONDS.sleep(new Random().nextInt(5));
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};

		FutureTask ft2 = new FutureTask(r, "Runnable");//give return value directly
		FutureTask ft3 = new FutureTask(c);
		FutureTask ft4 = new FutureTask(c);
		FutureTask ft5 = new FutureTask(c);
		FutureTask ft6 = new FutureTask(c);
		
		ExecutorService es = Executors.newFixedThreadPool(2);//init ExecutorService
		es.submit(ft2);
		es.submit(ft3);
		es.submit(ft4);
		es.submit(ft5);
		es.submit(ft6);
		
		
		
		try {
			TimeUnit.SECONDS.sleep(1);
			
			if(ft1.isDone()){				
				ft4.cancel(false);
				
				if(ft4.isCancelled()){
					System.out.println("task4 cancelled.");
				}
			}
			
			if(ft2.isDone()){				
				ft5.cancel(false);
				
				if(ft5.isCancelled()){
					System.out.println("task5 cancelled.");
				}
			}
			
			if(ft3.isDone()){				
				ft6.cancel(false);
				
				if(ft6.isCancelled()){
					System.out.println("task5 cancelled.");
				}
			}
							
			System.out.println("task1 retult:" + ft1.get());
			System.out.println("task2 retult:" + ft2.get());
			System.out.println("task3 retult:" + ft3.get());
			
			if(! ft4.isCancelled()){
				System.out.println("task4 retult:" + ft4.get());
			}
			
			if(! ft5.isCancelled()){
				System.out.println("task5 retult:" + ft5.get());
			}
			
			if(! ft6.isCancelled()){
				System.out.println("task6 retult:" + ft6.get());
			}	
			
			es.shutdown();//shut down ExecutorService
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}

	}
}