Javaの基本的なCallable、Future、FutureTask、CompletionServiceの使用

序文

この記事では、Callable、Future、FutureTask、CompletionServiceの基本的な使用法を紹介します。スレッドタスクを作成する方法は3つあります。最初の2つは実行可能で、3つ目はCallableを使用する方法です。
Runnableと比較して、Callableには、Callableを使用すると、ウェイクアップしてRunnableの結果を取得するのを待つのではなく、スレッドをブロックして戻り結果を取得できるという利点があります。Androidでは、Handlerを使用して戻り結果をラップします。
この記事では、これらのカテゴリについて簡単に説明します。多くの使用シナリオを学び、構成する必要があります。

1.基本的なインターフェースの紹介

// 执行Callable返回结果V
public interface Callable<V> {
    
    
    V call() throws Exception;
}

// 操作执行结果
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;
}

/*
   实现了Runnable和Future,即可以被线程启动、也可以被线程池启动,也可以获取返回结果
    FutureTask即是它的实现类,看下源码便知道get()方法是怎样实现的,也是通过等待唤醒,即是阻塞的,使用时要注意
*/
public interface RunnableFuture<V> extends Runnable, Future<V> {
    
    
    void run();
}

// RunnableFuture的实现类,封装Callable或Runnable,在run()里执行callable.call,使用等待唤醒获取返回值。因为实现了Runnable接口,所以也可以用Thread直接启动
public class FutureTask<V> implements RunnableFuture<V> {
    
    
    public FutureTask(Callable<V> callable) {
    
    
          // ...
    }

    public FutureTask(Runnable runnable, V result) {
    
    
          // ...
    }
}

// 线程池
public abstract class AbstractExecutorService implements ExecutorService {
    
    
    // 封装callable
    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
    
    
        return new FutureTask<T>(runnable, value);
    }

    // 封装callable
    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
    
    
        return new FutureTask<T>(callable);
    }

    // 提交Runnable,
    public Future<?> submit(Runnable task) {
    
    
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

    // 提交Runnable,result表示泛型
    public <T> Future<T> submit(Runnable task, T result) {
    
    
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }

    // 提交Callable
    public <T> Future<T> submit(Callable<T> task) {
    
    
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }
}

2. Callable、Future、FutureTaskの基本的な使用法

2.1Callableを使用する

Callableはスレッドで開始できません。結局のところ、Threadクラスにはこのコンストラクターがありません。スレッドプールを通過した後submit、Futureオブジェクトが返され、結果が取得されます。実際、スレッドプールが返すのはFutureTaskオブジェクトですが、インターフェイスを返します。

private static void test() {
    
    
    ExecutorService threadPoolExecutor = Executors.newSingleThreadExecutor();
    Future<String> result = threadPoolExecutor.submit(new Callable<String>() {
    
    
        @Override
        public String call() throws Exception {
    
    
            System.out.println("子线程执行1");

            Thread.sleep(2000);

            int sum = 0;
            for (int i = 0; i < 10000; i++) {
    
    
                sum += i;
            }

            System.out.println("子线程执行2");

            return String.valueOf(sum);
        }
    });

   result.get(); // 阻塞获取返回值
}

2.2FutureTaskを使用する

private static void  test() {
    
    
    ExecutorService threadPoolExecutor = Executors.newSingleThreadExecutor();
    // 创建FutureTask
    FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
    
    
        @Override
        public String call() throws Exception {
    
    
            System.out.println("子线程执行1");

            Thread.sleep(2000);

            int sum = 0;
            for (int i = 0; i < 10000; i++) {
    
    
                sum += i;
            }

            System.out.println("子线程执行2");
            return String.valueOf(sum);
        }
    });
    
    // 通过线程池启动
    Future<?> submit = threadPoolExecutor.submit(futureTask);
    try {
    
    
        System.out.println("submit: " + submit.get()); // 无结果
    } catch (InterruptedException | ExecutionException e) {
    
    
        e.printStackTrace();
    }

    // 通过线程启动,两者只能选用一个
    // new Thread(futureTask).start();

   futureTask.get(); //  阻塞获取返回值
}

3.ExecutorCompletionServiceの使用

将来の実行のデメリット:スレッドプールに大きなプッシュがスローされ、結果を取得するには結果をトラバースする必要があります。遅いスレッドがブロックされている場合、最初に実行されたスレッドもブロックされます。ExecutorCompletionServiceを使用すると、この欠点を回避し、実行後に最初に戻ることができます。

/*
    使用ExecutorCompletionService,谁快谁先出来
        主线程 start
        子线程执行: 1, pool-1-thread-2
        子线程执行: 0, pool-1-thread-1
        子线程执行: 3, pool-1-thread-4
        子线程执行: 5, pool-1-thread-4
        子线程执行: 7, pool-1-thread-5
        子线程执行: 9, pool-1-thread-6
        主线程获取执行结果: 0, 1
        主线程获取执行结果: 1, 0
        主线程获取执行结果: 2, 3
        主线程获取执行结果: 3, 5
        主线程获取执行结果: 4, 7
        主线程获取执行结果: 5, 9
        子线程执行: 2, pool-1-thread-3
        主线程获取执行结果: 6, 2
        子线程执行: 4, pool-1-thread-2
        主线程获取执行结果: 7, 4
        子线程执行: 6, pool-1-thread-1
        主线程获取执行结果: 8, 6
        子线程执行: 8, pool-1-thread-4
        主线程获取执行结果: 9, 8
 */
private static void testExecutorCompletionService() {
    
    
    ExecutorService executor = Executors.newCachedThreadPool();
    
    ExecutorCompletionService<String> executorCompletionService = new ExecutorCompletionService<>(executor);

    int count = 10;
    for (int i = 0; i < count; ++i) {
    
    
    	 // 使用ExecutorCompletionService提交任务
        executorCompletionService.submit(new NumberCallable(i));
    }

    try {
    
    
        for (int i = 0; i < count; ++i) {
    
    
        	 // 使用ExecutorCompletionService#take()获取结果
            System.out.println("主线程获取执行结果: " + i + ", " + executorCompletionService.take().get());
        }
    } catch (Exception e) {
    
    
        e.printStackTrace();
    }
}

/*
    普通执行,按顺序执行:
        主线程 start
        子线程执行: 0, pool-1-thread-1
        子线程执行: 1, pool-1-thread-2
        子线程执行: 3, pool-1-thread-1
        子线程执行: 5, pool-1-thread-1
        子线程执行: 7, pool-1-thread-1
        子线程执行: 9, pool-1-thread-1
        主线程获取执行结果: 0, 0
        主线程获取执行结果: 1, 1
        子线程执行: 2, pool-1-thread-2
        主线程获取执行结果: 2, 2
        主线程获取执行结果: 3, 3
        子线程执行: 4, pool-1-thread-3
        主线程获取执行结果: 4, 4
        主线程获取执行结果: 5, 5
        子线程执行: 6, pool-1-thread-4
        主线程获取执行结果: 6, 6
        主线程获取执行结果: 7, 7
        子线程执行: 8, pool-1-thread-5
        主线程获取执行结果: 8, 8
        主线程获取执行结果: 9, 9
 */
private static void testFutures() {
    
    
    ExecutorService executorService = Executors.newCachedThreadPool();
    List<Future<String>> list = new ArrayList<>();

    for (int i = 0; i < 10; i++) {
    
    
        Future<String> submit = executorService.submit(new NumberCallable(i));
        list.add(submit);
    }

    try {
    
    
        for (int i = 0; i < list.size(); i++) {
    
    
            System.out.println("主线程获取执行结果: " + i + ", " + list.get(i).get());
        }
    } catch (Exception e) {
    
    
        // do nothing
    }

    executorService.shutdown();
}

結論:

Runnableが最も使用されますが、Callable、Future、FutureTaskも使用されます。シナリオによっては、多くのものが保存され、結果を得るためにウェイクアップを待つなどの方法を使用する必要はありません。これらのシナリオでは、それはより効果的かもしれません。

おすすめ

転載: blog.csdn.net/u014099894/article/details/85228318