CompletionService学习

本文详细探讨了FutureTask在不同JDK版本中的实现变迁,从AQS到CAS的改进,以及CompletionService如何整合Executor与BlockingQueue特性,提供更高效的任务处理与结果获取方式。

前面已经说到Future的默认实现是FutureTask,因此你可以看到其在jdk1.5的时候采用的是AQS去实现的,因此具有阻塞性,但jdk1.6之后,可以看到其基于CAS实现的。之所以学习Future,除了其具备异步功能,同时其采用的思想也是在设计模式中有体现的,也即Future模式,而且可以在kafka源码中看到基于Future构建的异步编程。

前面说到其基于AQS具有阻塞性,但从源码中,可以看到在jdk1.6之后采用的是CAS:

public V get(long timeout, TimeUnit unit)
    throws InterruptedException, ExecutionException, TimeoutException {
    if (unit == null)
        throw new NullPointerException();
    int s = state;
    if (s <= COMPLETING &&
        (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
        throw new TimeoutException();
    return report(s);
}

可以看到awaitDone(true, unit.toNanos(timeout)))方法:

  /**
     * Awaits completion or aborts on interrupt or timeout.
     *
     * @param timed true if use timed waits
     * @param nanos time to wait, if timed
     * @return state upon completion
     */
    private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        WaitNode q = null;
        boolean queued = false;
        for (;;) {  //进行自旋
            if (Thread.interrupted()) {
                removeWaiter(q);
                throw new InterruptedException();
            }

            int s = state;  //进行状态匹配
            if (s > COMPLETING) {
                if (q != null)
                    q.thread = null;
                return s;
            }
            else if (s == COMPLETING) // cannot time out yet
                Thread.yield();
            else if (q == null)
                q = new WaitNode();
            else if (!queued)
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                     q.next = waiters, q);
            else if (timed) {
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) {
                    removeWaiter(q);
                    return state;
                }
                LockSupport.parkNanos(this, nanos);
            }
            else
                LockSupport.park(this);
        }
    }

还有其run方法:

public void run() {//采用cas
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
                set(result);
        }
    } finally {
        // runner must be non-null until state is settled to
        // prevent concurrent calls to run()
        runner = null;
        // state must be re-read after nulling runner to prevent
        // leaked interrupts
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

也即可以看到jdk1.6之后,FutureTask采用park和cas来实现的。

下面来学习CompletionService。

如果向Executor提交一组计算任务,并且希望在计算完成后获得结果,那么可以保留与每个任务关联的future,然后返回使用get方法,同时将timeout指定为0,从而通过轮询的方式来判断任务是否完成,但是这样有些繁琐。因此可以采用CompletionService,其将Executor和BlockingQueue的功能融合在一起,可以采用队列操作take、poll获取已完结的结果。 --摘自《并发编程实战》

/**
 * @description: CompletionSerivce使用
 * <p>
 * 采用异步的方式一边处理新的任务,一边处理完成任务的结果
 * 也就是说在处理多个任务时,可以实现先处理的任务,先拿到结果
 * 采用 submit+take,不至于在一个任务没有完成的情况下,其余的结果不能处理
 * 你可以将其理解成Executor+BlockingQueue的结合体,此时你可以使用其实现
 * ExecutorCompletionService,进行异构并行
 * </p>
 * @author: lyz
 * @date: 2020/05/24 22:02
 **/
public class CompletionServiceTest {
    public static void main(String[] args) {
        Long start = System.currentTimeMillis();
        //开启5个线程
        ExecutorService exs = Executors.newFixedThreadPool(4);
        try {
            int taskCount = 10;
            // 结果集
            List<Integer> list = new ArrayList<Integer>();
            List<Future<Integer>> futureList = new ArrayList<Future<Integer>>();

            // 1.定义CompletionService
            CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(exs);

            // 2.添加任务,需要执行的业务
            for (int i = 0; i < taskCount; i++) {
                Future<Integer> future = completionService.submit(new Task(i + 1));
                futureList.add(future);
            }

            // 3.获取结果
            for (int i = 0; i < taskCount; i++) {
                Integer result = completionService.take().get();
                System.out.println("任务i==" + result + "完成!" + new Date());
                list.add(result);
            }

            System.out.println("list=" + list);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭线程池
            exs.shutdown();
        }

    }

    //实现Callable接口,重写call方法
    static class Task implements Callable<Integer> {
        Integer i;

        public Task(Integer i) {
            super();
            this.i = i;
        }

        @Override
        public Integer call() throws Exception {
            if (i == 4) {
                Thread.sleep(5000);
            } else {
                Thread.sleep(1000);
            }
            System.out.println("线程:" + Thread.currentThread().getName() + "任务i=" + i + ",执行完成!");
            return i;
        }

    }
}

运行结果:

线程:pool-1-thread-2任务i=2,执行完成!
线程:pool-1-thread-1任务i=1,执行完成!
线程:pool-1-thread-3任务i=3,执行完成!
任务i==2完成!Sun May 24 22:50:01 CST 2020
任务i==1完成!Sun May 24 22:50:01 CST 2020
任务i==3完成!Sun May 24 22:50:01 CST 2020
线程:pool-1-thread-2任务i=5,执行完成!
任务i==5完成!Sun May 24 22:50:02 CST 2020
线程:pool-1-thread-1任务i=6,执行完成!
线程:pool-1-thread-3任务i=7,执行完成!
任务i==6完成!Sun May 24 22:50:02 CST 2020
任务i==7完成!Sun May 24 22:50:02 CST 2020
线程:pool-1-thread-2任务i=8,执行完成!
任务i==8完成!Sun May 24 22:50:03 CST 2020
线程:pool-1-thread-1任务i=9,执行完成!
线程:pool-1-thread-3任务i=10,执行完成!
任务i==9完成!Sun May 24 22:50:03 CST 2020
任务i==10完成!Sun May 24 22:50:03 CST 2020
线程:pool-1-thread-4任务i=4,执行完成!
任务i==4完成!Sun May 24 22:50:05 CST 2020
list=[2, 1, 3, 5, 6, 7, 8, 9, 10, 4]

可以看到其与FutureTask相比,运行的结果是不相同的,其返回结果的线程是乱序的,同时是先执行完的,先返回结果,不同于FutureTask的顺序返回。同时性能上优于FutureTask。当然,在JDK8中,我们可以是一个更为强大的CompletableFuture来实现异构应用。

<think>首先,用户的问题是关于Java并发API中的一些类:"Future|FutureTask| CompletionService|CompletableFuture 的区别和用法"。我需要用中文回答问题。 问题描述:"Future|FutureTask| CompletionService|CompletableFuture 的区别和用法"。意思是我需要解释这些概念的区别和用法。 关键元素: - Future:Java中的一个接口,表示异步计算的结果。 - FutureTask:一个实现了Future和Runnable的类,可以用于包装Callable或Runnable任务。 - CompletionService:一个接口,用于管理已完成任务的队列,通常与ExecutorService一起使用。 - CompletableFuture:Java 8引入的一个类,提供了更强大的异步编程支持,支持回调、组合等。 回答结构: 1. **区别**:清晰地解释每个概念是什么,以及它们之间的主要区别。 2. **用法**:简要说明如何使用每个工具,包括示例代码。 用户指定了输出格式:回答完问题后,必须提出3-5个相关问题,输出在末尾,第一行必须是"§§相关问题§§",禁止换行分散。相关问题不能缺少。 从示例中: - 回答以一个描述开始,然后可能有代码。 - 最后以"§§相关问题§§"开头,跟着1、2、3等列表的问题。 所以,我的回答应该: - 完整回答问题。 - 在末尾添加"§§相关问题§§",然后列出3-5个相关问题。 - 使用中文。 相关问题应该基于用户的问题和我的回答。理想情况下,它们是自然延伸,帮助用户深入学习。 大纲回答: 1. 介绍所有概念。 2. 解释区别: - Future:基本接口,用于检查异步任务是否完成,获取结果。 - FutureTask:Future的实现类,可以作为任务提交给Executor。 - CompletionService:帮助管理多个异步任务的完成顺序,按完成顺序获取结果。 - CompletableFuture:扩展Future,支持链式调用、组合、异常处理等。 3. 用法示例:提供简短的代码片段说明如何使用。 - 例如,Future使用ExecutorService提交Callable。 - FutureTask的例子。 - CompletionService的例子。 - CompletableFuture的例子。 确保代码用中文注释或描述,因为用户要求中文。 最后,相关问题: - 3-5个,例如: 1. Future接口的主要方法有哪些? 2. 如何在Java中使用CompletableFuture处理异常? 3. CompletionService与ExecutorService的区别是什么? 4. FutureTask和CompletableFuture哪个更适合复杂异步流程? 5. 如何组合多个CompletableFuture任务? 现在,构建回答。 开头:直接切入主题,解释区别。 </think>### Future、FutureTask、CompletionService 和 CompletableFuture 的区别和用法 在 Java 并发编程中,`Future`、`FutureTask`、`CompletionService` 和 `CompletableFuture` 都是用于处理异步任务的工具。它们各有特点和适用场景,下面详细解释它们的区别和基本用法。 #### 区别 1. **Future**: - **定义**:一个接口(`java.util.concurrent.Future`),表示异步计算的结果。它提供方法来检查任务是否完成、等待结果或取消任务。 - **特点**:简单但功能有限,只能用于查询任务状态或获取结果,不支持回调或链式操作。 - **适用场景**:适用于单个异步任务的简单监控。 2. **FutureTask**: - **定义**:一个类(`java.util.concurrent.FutureTask`),实现了 `Future` 和 `Runnable` 接口。它可以包装 `Callable` 或 `Runnable` 任务,并作为任务提交给 `ExecutorService`。 - **特点**:集成了 `Future` 的功能,同时可作为任务运行。比纯 `Future` 更灵活,但缺乏高级组合能力。 - **适用场景**:用于手动创建和提交任务,适合需要对任务进行包装的场景。 3. **CompletionService**: - **定义**:一个接口(`java.util.concurrent.CompletionService`),基于 `ExecutorService` 构建,用于管理多个异步任务的完成顺序。它提供了一个队列来按任务完成顺序获取结果。 - **特点**:专注于处理多个任务的完成事件,按 FIFO(先进先出)顺序获取结果,简化批量任务处理。 - **适用场景**:适合处理大批量异步任务,需要按完成顺序处理结果的场景,如并行数据处理。 4. **CompletableFuture**: - **定义**:一个类(`java.util.concurrent.CompletableFuture`),从 Java 8 引入,扩展了 `Future` 接口。支持链式调用、组合多个任务、回调函数和异常处理。 - **特点**:功能最强大,支持非阻塞异步编程,如 `thenApply()`、`thenCompose()`、`allOf()` 等方法,实现复杂流程。 - **适用场景**:适合现代异步编程,如微服务调用、事件驱动系统,需要任务组合和错误恢复的场景。 **核心区别总结**: - **功能层级**:`Future` 是基础接口; `FutureTask` 是其可运行实现; `CompletionService` 是任务完成管理工具; `CompletableFuture` 是高级异步编程框架。 - **异步支持**:`Future` 和 `FutureTask` 需要手动轮詢结果; `CompletionService` 自动排序完成的任务; `CompletableFuture` 支持回调式非阻塞编程。 - **复杂度**:`Future`/`FutureTask` 简单但笨重; `CompletionService` 中等,适合批量任务; `CompletableFuture` 灵活但学习曲线陡峭。 #### 用法示例 以下为简单代码示例,展示基本用法(使用 Java 语法)。 ```java import java.util.concurrent.*; // Future 的用法:提交任务到线程池并获取结果 ExecutorService executor = Executors.newFixedThreadPool(2); Future<String> future = executor.submit(() -> "Hello from Future"); String result = future.get(); // 阻塞获取结果 System.out.println(result); // FutureTask 的用法:手动创建并运行任务 FutureTask<String> futureTask = new FutureTask<>(() -> "Hello from FutureTask"); new Thread(futureTask).start(); // 启动线程 System.out.println(futureTask.get()); // 获取结果 // CompletionService 的用法:处理多个任务完成顺序 CompletionService<String> completionService = new ExecutorCompletionService<>(executor); completionService.submit(() -> "Task1"); completionService.submit(() -> "Task2"); for (int i = 0; i < 2; i++) { Future<String> completedFuture = completionService.take(); // 按完成顺序获取 System.out.println(completedFuture.get()); } // CompletableFuture 的用法:链式组合任务 CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello") .thenApply(s -> s + " from CompletableFuture") // 链式处理 .exceptionally(ex -> "Error: " + ex.getMessage()); // 异常处理 System.out.println(completableFuture.join()); // 非阻塞获取结果 executor.shutdown(); // 关闭线程池 ``` 在实际应用中,根据需求选择: - 简单任务:用 `Future` 或 `FutureTask`。 - 批量任务顺序处理:用 `CompletionService`。 - 复杂异步流:用 `CompletableFuture`。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值