FutureとFutureTaskは非同期計算を実現
5334 ワード
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オブジェクトから実行ステータスを問い合わせることができます.
Callableインタフェース:instanceはExecutorServiceのスレッドプールでしか走ることができませんが、返された結果がある場合は、返されたFutureオブジェクトでステータスを問い合わせることもできます.表面的にはCallableインタフェースを戻り結果のあるRunnalbeインタフェースと簡単に理解できる.
ExecutorServiceインタフェース:スレッドプール実行スケジューラ
Futureインタフェース:タスクの実行ステータスを照会したり、実行結果を取得したり、未実行のタスクをキャンセルしたりします.ExecutorServiceフレームワークでは、スレッドプールを使用するため、RunnableインスタンスとCallableインスタンスは「スレッド」ではなくタスクとして扱われるため、Futureではタスク実行のキャンセルなどのインタフェースがあります.インタフェースのget()メソッドは、タスクの実行結果を取得するために使用されます.タスクは非同期で実行されるため、結果を使用する必要がある場合にget()メソッドを呼び出すことができます.呼び出し時にタスクがまだ実行されていない場合、タスクが完了するまでブロックされます.もちろん、getの別のリロードバージョンget(long timeout,TimeUnit unit)を呼び出すこともできます.ブロックされると指定された時間を待つことができます.時間が来てもタスクが完了していない場合は、TimeoutExceptionを放出します.
FutureTaskクラス:Runnable、Callable、Futureを一体化し、まずRunnableとFutureインタフェースを実現し、次にコンストラクション関数にCallableオブジェクト(または変形したCallableオブジェクト:Runnable+Result)を注入するので、FutureTaskクラスはnew Thread(Runnable r)を使用して新しいスレッドに配置したり、ExecutorServices.submit(Runnable)を使用したりすることができます.スレッドプールに移動し、両方の方法で戻り結果を得ることができますが、実質的には同じです.つまり、戻り結果がある場合は、コンストラクション関数は必ずCallableオブジェクトに注入するか、Runnableオブジェクトに予め与えられた結果を注入するか(個人的にはあまり役に立たないと思います).
サンプルコード:
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();
}
}
}