Java基础 Callable、Future、FutureTask、CompletionService的使用

前言

本篇文章介绍Callable、Future、FutureTask、CompletionService的基本使用。创建线程任务的三种方式,之前两种都是Runnable,第三种就是使用Callable。
Callable相比于Runnable的好处来说,使用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.1 使用Callable

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.2 使用FutureTask

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使用

future执行的缺点: 一大推扔进线程池中,需要遍历获取结果,如果有慢的线程一直在阻塞,则后面先执行完的线程也要被阻塞。使用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