【并发编程】多线程对任务的编排:CompletableFuture

CompletableFuture是什么

  • 简单的任务,用Future获取结果还好,但我们并行提交的多个异步任务,往往并不是独立的,很多时候业务逻辑处理存在串行[依赖]、并行、聚合的关系。
  • CompletableFuture是Future接口的扩展和增强。
  • CompletableFuture实现了对任务的编排能力。
  • 虽然通过CountDownLatch等工具类也可以实现任务的编排,但需要复杂的逻辑处理,不仅耗费精力且难以维护。

CompletableFuture的主要功能介绍

  • 描述依赖关系:
  • thenApply:把前面异步任务的结果,交给后面的Function
  • thenCompose:用来连接两个有依赖关系的任务,结果由第二个任务返回
  • 描述and聚合关系:
  • thenCombine:任务合并,有返回值
  • thenAccepetBoth:两个任务执行完成后,将结果交给thenAccepetBoth消耗,无返回值。
  • runAfterBoth:两个任务都执行完成后,执行下一步操作(Runnable)。
  • 描述or聚合关系:
  • applyToEither:两个任务谁执行的快,就使用那一个结果,有返回值。
  • acceptEither: 两个任务谁执行的快,就消耗那一个结果,无返回值。
  • runAfterEither: 任意一个任务执行完成,进行下一步操作(Runnable)。
  • 并行执行:
  • CompletableFuture类自己也提供了anyOf()和allOf()用于支持多个CompletableFuture并行执行
  • 获取结果
  • join:无异常抛出
  • get:抛出的是经过检查的异常,ExecutionException, InterruptedException
  • 结果处理
  • whenComplete:正常的结果处理
  • exceptionally:异常的结果处理
  • whenCompleteAsync:正常的结果处理,开启新线程
  • 结果转换
  • thenApply:使用一阶段的结果用在二阶段,返回一个具有处理结果的Future对象。
  • thenCompose:使用一阶段的结果用在二阶段,返回 CompletableFuture 实例的函数,该函数的参数是先前计算步骤的结果。
  • 结果消费
  • thenAccept系列:对单个结果进行消费
  • thenAcceptBoth系列:对两个结果进行消费
  • thenRun系列:不关心结果,只对结果执行Action
  • 结果组合
  • thenCombine:合并两个线程任务的结果,并进一步处理。
  • 任务交互
  • applyToEither:两个线程任务相比较,先获得执行结果的,就对该结果进行下一步的转化操作。返回CompletionStage<U>。
  • acceptEither:两个线程任务相比较,先获得执行结果的,就对该结果进行下一步的消费操作。返回CompletionStage<Void>。
  • runAfterEither:两个线程任务相比较,有任何一个执行完成,就进行下一步操作,不关心运行结果。返回CompletionStage<Void>。
  • runAfterBoth:两个线程任务相比较,两个全部执行完成,才进行下一步操作,不关心运行结果。返回CompletionStage<Void>。
  • anyOf:方法的参数是多个给定的 CompletableFuture,当其中的任何一个完成时,方法返回这个 CompletableFuture。
  • allOf:方法用来实现多 CompletableFuture 的同时返回。

CompletableFuture的使用方式

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class CompletableFutureTest {

	public static void main(String[] args) throws ExecutionException, InterruptedException {

		// 执行无返回结果的异步任务
		CompletableFuture.runAsync(() -> System.out.println("执行无返回结果的异步任务"));

		// 执行有返回值的异步任务
		CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
		    System.out.println("执行有返回值的异步任务");
		    try {
		        Thread.sleep(5000);
		    } catch (InterruptedException e) {
		        e.printStackTrace();
		    }
		    return "Hello World";
		});
		// 打印有返回值异步任务的返回值
		System.out.println(future.get());
	}
}

CompletableFuture的执行结果.png

CompletableFuture的创建异步操作源码分析

/**
 * Supplier函数式接口类型为参数,有返回值,会阻塞
 */
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
    return asyncSupplyStage(asyncPool, supplier);
}

/**
 * Supplier函数式接口类型和具体使用的线程池为参数,有返回值,会阻塞
 */
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
                                                   Executor executor) {
    return asyncSupplyStage(screenExecutor(executor), supplier);
}

/**
 * 传入一个具体任务,执行的时候没有返回值!
 */
public static CompletableFuture<Void> runAsync(Runnable runnable) {
    return asyncRunStage(asyncPool, runnable);
}

/**
 * 传入一个具体任务和使用的线程池,执行的时候没有返回值
 */
public static CompletableFuture<Void> runAsync(Runnable runnable,
                                               Executor executor) {
    return asyncRunStage(screenExecutor(executor), runnable);
}

/**
 * 我们一般是多核处理器,如果不传入具体的线程池,则使用ForkJoinPool做为默认的线程池。
 */
private static final Executor asyncPool = useCommonPool ?
        ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();

/**
 * 有返回值的具体创建方式
 */
static <U> CompletableFuture<U> asyncSupplyStage(Executor e,
        Supplier<U> f) {
	// 传入的任务为null,抛异常
    if (f == null) throw new NullPointerException();
    // 创建一个CompletableFuture对象
    CompletableFuture<U> d = new CompletableFuture<U>();
    // 执行具体的任务:并通过CAS的方式设置结果
    e.execute(new AsyncSupply<U>(d, f));
    // 返回具体的结果
    return d;
}

/**
 * 无返回值的具体创建方式
 */
static CompletableFuture<Void> asyncRunStage(Executor e, Runnable f) {
    // 传入的任务weinull,抛异常
    if (f == null) throw new NullPointerException();
    // 创建一个CompletableFuture对象
    CompletableFuture<Void> d = new CompletableFuture<Void>();
    // 执行具体的任务:无任何设置结果的代码
    e.execute(new AsyncRun(d, f));
    // 返回具体的结果
    return d;
}

CompletableFuture的获取返回结果的方法join&get源码分析

/**
 * 获取结果的方法,不抛出异常
 */
public T join() {
    Object r;
    return reportJoin((r = result) == null ? waitingGet(false) : r);
}

/**
 * 抛出异常
 */
public T get() throws InterruptedException, ExecutionException {
    Object r;
    return reportGet((r = result) == null ? waitingGet(true) : r);
}

/**
 * join的获取方法
 */
private static <T> T reportJoin(Object r) {
    // 判断有没有异常,对各种异常进行判断并抛出
    if (r instanceof AltResult) {
        Throwable x;
        if ((x = ((AltResult)r).ex) == null)
            return null;
        if (x instanceof CancellationException)
            throw (CancellationException)x;
        if (x instanceof CompletionException)
            throw (CompletionException)x;
        throw new CompletionException(x);
    }
    // 强转并返回结果
    @SuppressWarnings("unchecked") T t = (T) r;
    return t;
}

/**
 * get的获取方法
 */
private static <T> T reportGet(Object r)
    throws InterruptedException, ExecutionException {
    // 结果不存在,抛异常
    if (r == null) // by convention below, null means interrupted
        throw new InterruptedException();
    // 判断有没有异常,对各种异常进行判断并抛出
    if (r instanceof AltResult) {
        Throwable x, cause;
        if ((x = ((AltResult)r).ex) == null)
            return null;
        if (x instanceof CancellationException)
            throw (CancellationException)x;
        if ((x instanceof CompletionException) &&
            (cause = x.getCause()) != null)
            x = cause;
        throw new ExecutionException(x);
    }
    // 强转并返回结果
    @SuppressWarnings("unchecked") T t = (T) r;
    return t;
}

/**
 * 等待获取最后的结果
 */
private Object waitingGet(boolean interruptible) {
    // Completion的子类,用于记录阻塞
    Signaller q = null;
    // 阻塞记录是否已放入任务栈中
    boolean queued = false;
    // 自旋的标志,-1代表未启动自旋,0代表已结束自旋,>0代表自旋进行中
    int spins = -1;
    // 具体返回的结果
    Object r;
    // 还没有结果的时候进入循环
    while ((r = result) == null) {
        // 自旋的标志小于0,说明未启动
        if (spins < 0)
            // private static final int SPINS = (Runtime.getRuntime().availableProcessors() > 1 ? 1 << 8 : 0);
            // 多核CPU这里的值是256,单核CPU的值为0
            spins = SPINS;
        // 自旋的标志大于0,说明正在自旋
        else if (spins > 0) {
            // 取一个随机数,看看要不要进行处理自旋次数
            if (ThreadLocalRandom.nextSecondarySeed() >= 0)
                --spins;
        }
        // 判断到这里,说明spins为0,准备进行阻塞
        // 阻塞队列中无内容的时候,进行创建队列
        else if (q == null)
            // 创建新的阻塞
            q = new Signaller(interruptible, 0L, 0L);
        // 不是第一次入队
        else if (!queued)
            // 入队操作
            queued = tryPushStack(q);
        // 判断当前的节点是否被中断
        else if (interruptible && q.interruptControl < 0) {
            // 取消线程的绑定
            q.thread = null;
            // 清楚队列中的当前节点
            cleanStack();
            // 返回结果为null
            return null;
        }
        // 还没有获取到结果
        else if (q.thread != null && result == null) {
            try {
                // 阻塞
                ForkJoinPool.managedBlock(q);
            } catch (InterruptedException ie) {
                // 阻塞中途出现问题,修改变量
                q.interruptControl = -1;
            }
        }
    }
    // 执行到这里说明获取到了值
    if (q != null) {
        // 释放线程
        q.thread = null;
        // 中断后的判断
        if (q.interruptControl < 0) {
            if (interruptible)
                r = null; // report interruption
            else
                Thread.currentThread().interrupt();
        }
    }
    // 将后续执行的任务唤醒。
    postComplete();
    // 返回具体的结果
    return r;
}

CompletableFuture的结果处理方法使用

import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.BiConsumer;
import java.util.function.Function;

public class CompletableFutureTest2 {

	public static void main(String[] args) throws ExecutionException, InterruptedException {

		CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			if (new Random().nextInt(10) % 2 == 0) {
				System.out.println("出现异常!");
				int i = 12 / 0;
			}
			System.out.println("执行结束!");
			return "test";
		});

		future.whenComplete(new BiConsumer<String, Throwable>() {
			@Override
			public void accept(String t, Throwable action) {
				System.out.println(t + " 执行完成!");
			}
		});

		future.exceptionally(new Function<Throwable, String>() {
			@Override
			public String apply(Throwable t) {
				System.out.println("执行失败:" + t.getMessage());
				return "异常xxxx";
			}
		});
	}
}

结束语

  • 获取更多本文的前置知识文章,以及新的有价值的文章,让我们一起成为架构师!
  • 关注公众号,可以让你对MySQL、并发编程、spring源码有深入的了解!
  • 关注公众号,后续持续高效的学习JVM!
  • 这个公众号,无广告!!!每日更新!!!
    作者公众号.jpg
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
多线程并发编程Java中一个重要的概念和技术。如果你对这方面的知识感兴趣,以下是一些推荐的书籍: 1. 《Java并发编程实战》:这本书被称为Java并发的圣经,它详细介绍了Java中的并发编程概念、问题和解决方案。\[3\] 2. 《Java多线程编程核心技术》:这本书介绍了Java多线程编程的核心技术,包括线程的创建、同步、通信等方面的内容。 3. 《Java 7并发编程实战手册》:这本书是一个实践指南,介绍了Java 7中的并发编程实践,包括使用Java并发工具和API等方面的内容。\[3\] 4. 《Java并发编程的艺术》:这本书介绍了在多核处理器的共享内存模型中的各种并发算法和技术。\[3\] 5. 《C++ Concurrency in Action》:这本书介绍了C++中的并发编程,包括线程的创建、同步、通信等方面的内容。\[3\] 这些书籍涵盖了多线程并发编程的核心概念、技术和实践,可以帮助你深入理解和应用这些知识。希望对你有帮助! #### 引用[.reference_title] - *1* [Java 并发和多线程](https://blog.csdn.net/m0_72674204/article/details/126332825)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [有什么好的并发编程书籍推荐?还真有一本](https://blog.csdn.net/epubit17/article/details/121733925)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [19本高并发编程书籍推荐](https://blog.csdn.net/liuhuiteng/article/details/106090113)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值