2-2-3-7、FutureCompletableFuture详解

news2025/1/19 23:22:28

Runnable

在这里插入图片描述


线程的任务接口,用于定义被执行任务方法的抽象,是函数式接口(仅存在一个需要实现方法的接口),其方法为run方法
通过对并发编程中java线程的了解,Thread调用start方法,最后操作系统会通过jvm回调当前Thread对象任务对象的run方法

缺点

  • 没有返回值
  • 不会抛出异常

Callable

在这里插入图片描述


用于定义可调用的方法的抽象,是函数式接口,其方法为call方法

Callable与Runnable比较

Callable接口类似于Runnable,因为它们都是为其实例可能由另一个线程执行的类设计的。但是,Runnable不返回结果,也不能抛出已检查的异常

Future

Future表示异步计算的结果。方法用于检查计算是否完成、等待计算完成并检索计算结果。结果只能在计算完成后使用get方法检索,必要时阻塞,直到它准备好。取消由cancel方法执行。还提供了其他方法来确定任务是正常完成还是被取消。一旦计算完成,就不能取消计算

在这里插入图片描述

所有方法

  • boolean cancel (boolean mayInterruptIfRunning)
    • 试图取消此任务的执行。如果任务已经完成、已经被取消或由于其他原因无法取消,则此尝试将失败。如果成功,并且在调用cancel时这个任务还没有启动,那么这个任务应该永远不会运行。如果任务已经开始,那么参数mayInterruptIfRunning决定在尝试停止任务时是否应该中断执行该任务的线程。 该方法返回后,对isDone的后续调用将始终返回true。如果该方法返回true,则对isCancelled的后续调用将始终返回true
  • boolean isCancelled ()
    • 如果该任务在正常完成之前被取消,则返回true
  • boolean isDone ()
    • 如果任务完成,返回true。完成可能是由于正常终止、异常或取消——在所有这些情况下,此方法将返回true
  • V get () throws InterruptedException, ExecutionException
    • 如果需要,等待计算完成,然后检索其结果
    • CancellationException -如果计算被取消
    • ExecutionException -如果计算抛出异常
    • InterruptedException -如果当前线程在等待时被中断
  • V get (long timeout, TimeUnit unit) throws InterruptedException,ExecutionException, TimeoutException
    • 如果需要,最多等待给定的时间来完成计算,然后检索其结果(如果可用)
    • CancellationException -如果计算被取消
    • ExecutionException -如果计算抛出异常
    • InterruptedException -如果当前线程在等待时被中断
    • TimeoutException -如果等待超时

RunnableFuture

Runnable和Future的组合接口,实现当前接口的方法具有对任务的执行、取消、获取返回结果等操作

FutureTask

在这里插入图片描述


基于RunnableFuture实现的一个对异步任务操作的类,相当于:

  1. 消费者和生产者的桥梁,消费者通过FutureTask 存储任务的处理结果,更新任务的状态:未开始、正在处理、已完成等。而生产者拿到的 FutureTask 被转型为 Future 接口,可以阻塞式获取任务的处理结果,非阻塞式获取任务处理状态
  2. FutureTask既可以被当做Runnable来执行,也可以被当做Future来获取Callable的返回结果

使用
把 Callable 实例当作 FutureTask 构造函数的参数,生成 FutureTask 的对象,然后把这个对象当作一个 Runnable 对象,放到线程池中或另起线程去执行,最后还可以通过FutureTask 获取任务执行的结果
代码示例:

public class FutureTaskDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Task task = new Task();
        //构建futureTask
        FutureTask<Integer> futureTask = new FutureTask<>(task);
        //作为Runnable入参
        new Thread(futureTask).start();

        System.out.println("task运行结果:"+futureTask.get());
    }
    static class Task implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            System.out.println("子线程正在计算");
            int sum = 0;
            for (int i = 0; i < 100; i++) {
                sum += i;
            }
            return sum;
        }
    }
}

运行结果:

子线程正在计算
task运行结果:4950

应用场景

促销活动中商品信息查询

在维护促销活动时需要查询商品信息(包括商品基本信息、商品价格、商品库存、商品图片、商品销售状态等)。这些信息分布在不同的业务中心,由不同的系统提供服务。如果采用同步方式,假设一个接口需要50ms,那么一个商品查询下来就需要200ms-300ms,这对于我们来说是不满意的。如果使用Future改造则需要的就是最长耗时服务的接口,也就是50ms左右

在这里插入图片描述


代码示例:

@Slf4j
public class FutureTaskDemo2 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> ft1 = new FutureTask<>(new T1Task());
        FutureTask<String> ft2 = new FutureTask<>(new T2Task());
        FutureTask<String> ft3 = new FutureTask<>(new T3Task());
        FutureTask<String> ft4 = new FutureTask<>(new T4Task());
        FutureTask<String> ft5 = new FutureTask<>(new T5Task());
        //构建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        executorService.submit(ft1);
        executorService.submit(ft2);
        executorService.submit(ft3);
        executorService.submit(ft4);
        executorService.submit(ft5);
        //获取执行结果
        log.info(ft1.get());
        log.info(ft2.get());
        log.info(ft3.get());
        log.info(ft4.get());
        log.info(ft5.get());
        executorService.shutdown();
    }
    static class T1Task implements Callable<String> {
        @Override
        public String call() throws Exception {
            log.info("T1:查询商品基本信息...");
            TimeUnit.MILLISECONDS.sleep(5000);
            return "商品基本信息查询成功";
        }
    }
    static class T2Task implements Callable<String> {
        @Override
        public String call() throws Exception {
            log.info("T2:查询商品价格...");
            TimeUnit.MILLISECONDS.sleep(50);
            return "商品价格查询成功";
        }
    }
    static class T3Task implements Callable<String> {
        @Override
        public String call() throws Exception {
            log.info("T3:查询商品库存...");
            TimeUnit.MILLISECONDS.sleep(50);
            return "商品库存查询成功";
        }
    }
    static class T4Task implements Callable<String> {
        @Override
        public String call() throws Exception {
            log.info("T4:查询商品图片...");
            TimeUnit.MILLISECONDS.sleep(50);
            return "商品图片查询成功";
        }
    }
    static class T5Task implements Callable<String> {
        @Override
        public String call() throws Exception {
            log.info("T5:查询商品销售状态...");
            TimeUnit.MILLISECONDS.sleep(50);
            return "商品销售状态查询成功";
        }
    }
}

运行结果:

16:48:37.316 [pool-1-thread-2] INFO com.example.demo.seven_two_six.concurrent.future.FutureTaskDemo2 - T2:查询商品价格…
16:48:37.316 [pool-1-thread-3] INFO com.example.demo.seven_two_six.concurrent.future.FutureTaskDemo2 - T3:查询商品库存…
16:48:37.316 [pool-1-thread-1] INFO com.example.demo.seven_two_six.concurrent.future.FutureTaskDemo2 - T1:查询商品基本信息…
16:48:37.316 [pool-1-thread-4] INFO com.example.demo.seven_two_six.concurrent.future.FutureTaskDemo2 - T4:查询商品图片…
16:48:37.316 [pool-1-thread-5] INFO com.example.demo.seven_two_six.concurrent.future.FutureTaskDemo2 - T5:查询商品销售状态…
16:48:42.330 [main] INFO com.example.demo.seven_two_six.concurrent.future.FutureTaskDemo2 - 商品基本信息查询成功
16:48:42.330 [main] INFO com.example.demo.seven_two_six.concurrent.future.FutureTaskDemo2 - 商品价格查询成功
16:48:42.330 [main] INFO com.example.demo.seven_two_six.concurrent.future.FutureTaskDemo2 - 商品库存查询成功
16:48:42.330 [main] INFO com.example.demo.seven_two_six.concurrent.future.FutureTaskDemo2 - 商品图片查询成功
16:48:42.330 [main] INFO com.example.demo.seven_two_six.concurrent.future.FutureTaskDemo2 - 商品销售状态查询成功

注意事项

  1. 当 for 循环批量获取 FutureTask 的结果时容易 block,get 方法调用时应使用 timeout限制
  2. FutureTask 的生命周期不能后退。 一旦完成了任务,它就永久停在了“已完成”的状态,不能从头再来

局限性

从本质上说,Future表示一个异步计算的结果。 它提供了isDone()来检测计算是否已经完成,并且在计算结束后,可以通过get()方法来获取计算结果。在异步计算中,Future确实是个非常优秀的接口。但是,它的本身也确实存在着许多限制:

  • 并发执行多任务:Future只提供了get()方法来获取结果,并且是阻塞的。所以,除了等待你别无他法
  • 无法对多个任务进行链式调用:如果你希望在计算任务完成后执行特定动作,比如发邮件,但Future却没有提供这样的能力
  • 无法组合多个任务:如果你运行了10个任务,并期望在它们全部执行结束后执行特定动作,那么在Future中这是无能为力的
  • 没有异常处理:Future接口中没有关于异常处理的方法

CompletionService

Callable+Future 可以实现多个task并行执行,但是如果遇到前面的task执行较慢时需要阻塞等待前面的task执行完后面task才能取得结果。而CompletionService的主要功能就是一边生成任务,一边获取任务的返回值。让两件事分开执行,任务之间不会互相阻塞,可以实现先执行完的先取结果,不再依赖任务顺序了

在这里插入图片描述

实现

ExecutorCompletionService

在这里插入图片描述


使用提供的Executor执行任务的CompletionService。这个类安排提交的任务在完成后被放置在使用take可访问的队列中。这个类非常轻量级,适合在处理任务组时临时使用

原理

内部通过阻塞队列+FutureTask,实现了任务先完成可优先获取到,即结果按照完成先后顺序排序,内部有一个先进先出的阻塞队列,用于保存已经执行完成的Future,通过调用它的take方法或poll方法可以获取到一个已经执行完成的Future,进而通过调用Future接口实现类的get方法获取最终的结果

应用场景

询价应用

向不同电商平台询价,并保存价格
采用“ThreadPoolExecutor+Future”的方案:异步执行询价然后再保存
代码示例:

// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
// 异步向电商S1询价
Future<Integer> f1 = executor.submit(()>getPriceByS1());
// 异步向电商S2询价
Future<Integer> f2= executor.submit(()>getPriceByS2());
// 获取电商S1报价并异步保存
executor.execute(()>save(f1.get()));
// 获取电商S2报价并异步保存
executor.execute(()>save(f2.get())

如果获取电商S1报价的耗时很长,那么即便获取电商S2报价的耗时很短,也无法让保存S2报价的操作先执行,因为这个主线程都阻塞 在了f1.get()操作上
使用CompletionService实现先获取的报价先保存到数据库
代码示例:

@Slf4j
public class CompletionServiceDemo {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        //创建线程池
        ExecutorService executor = Executors.newFixedThreadPool(10);
        //创建CompletionService
        CompletionService<Integer> cs = new ExecutorCompletionService<>(executor);
        //异步向电商S1询价
        cs.submit(CompletionServiceDemo::getPriceByS1);
        //异步向电商S2询价
        cs.submit(CompletionServiceDemo::getPriceByS2);
        //异步向电商S3询价
        cs.submit(CompletionServiceDemo::getPriceByS3);
        //将询价结果异步保存到数据库
        for (int i = 0; i < 3; i++) {
            //从阻塞队列获取futureTask
            Integer r = cs.take().get();
            executor.execute(() -> save(r));
        }
        executor.shutdown();
    }
    private static void save(Integer r) {
        log.debug("保存询价结果:{}",r);
    }
    private static Integer getPriceByS1() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(5000);
        log.debug("电商S1询价信息1200");
        return 1200;
    }
    private static Integer getPriceByS2() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(8000);
        log.debug("电商S2询价信息1000");
        return 1000;
    }
    private static Integer getPriceByS3()  throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(3000);
        log.debug("电商S3询价信息800");
        return 800;
    }
}

运行结果:

17:11:41.468 [pool-1-thread-3] DEBUG com.example.demo.seven_two_six.concurrent.future.CompletionServiceDemo - 电商S3询价信息800
17:11:41.471 [pool-1-thread-4] DEBUG com.example.demo.seven_two_six.concurrent.future.CompletionServiceDemo - 保存询价结果:800
17:11:43.470 [pool-1-thread-1] DEBUG com.example.demo.seven_two_six.concurrent.future.CompletionServiceDemo - 电商S1询价信息1200
17:11:43.470 [pool-1-thread-5] DEBUG com.example.demo.seven_two_six.concurrent.future.CompletionServiceDemo - 保存询价结果:1200
17:11:46.468 [pool-1-thread-2] DEBUG com.example.demo.seven_two_six.concurrent.future.CompletionServiceDemo - 电商S2询价信息1000
17:11:46.468 [pool-1-thread-6] DEBUG com.example.demo.seven_two_six.concurrent.future.CompletionServiceDemo - 保存询价结果:1000

实现类似 Dubbo 的 Forking Cluster场景

Dubbo 中有一种叫做 Forking 的集群模式,这种集群模式下,支持并行地调用多个服务实例,只要有一个成功就返回结果
代码示例:

@Slf4j
public class CompletionServiceDemo2 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // 创建线程池
        ExecutorService executor = Executors.newFixedThreadPool(3);
        // 创建CompletionService
        CompletionService<Integer> cs = new ExecutorCompletionService<>(executor);
        // 用于保存Future对象
        List<Future<Integer>> futures = new ArrayList<>(3);
        //提交异步任务,并保存future到futures
        futures.add(cs.submit(CompletionServiceDemo2::geocoderByS1));
        futures.add(cs.submit(CompletionServiceDemo2::geocoderByS2));
        futures.add(cs.submit(CompletionServiceDemo2::geocoderByS3));
        // 获取最快返回的任务执行结果
        Integer r = 0;
        try {
            // 只要有一个成功返回,则break
            for (int i = 0; i < 3; ++i) {
                r = cs.take().get();
                //简单地通过判空来检查是否成功返回
                if (r != null) {
                    break;
                }
            }
        } finally {
            //取消所有任务
            for (Future<Integer> f : futures)
                f.cancel(true);
        }
        executor.shutdown();
        System.out.println(r);
    }
    private static Integer geocoderByS1() throws InterruptedException {
        TimeUnit.SECONDS.sleep(1);
        return 1000;
    }
    private static Integer geocoderByS2() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(500);
        return 500;
    }
    private static Integer geocoderByS3() throws InterruptedException {
        TimeUnit.SECONDS.sleep(2);
        return 2000;
    }
}
//运行结果:500

使用场景总结

  • 当需要批量提交异步任务的时候建议你使用CompletionService。 CompletionService将线程池Executor和阻塞队列BlockingQueue的功能融合在了一起,能够让批量异步任务的管理更简单
  • CompletionService能够让异步任务的执行结果有序化。先执行完的先进入阻塞队列,利用这个特性,你可以轻松实现后续处理的有序性,避免无谓的等待,同时还可以快速实现诸如Forking Cluster这样的需求
  • 线程池隔离。 CompletionService支持自己创建线程池,这种隔离性能避免几个特别耗时的任务拖垮整个应用的风险

CompletableFuture

在这里插入图片描述

CompletionStage

异步计算的一个阶段,它在另一个CompletionStage完成时执行一个动作或计算一个值。一个阶段在其计算终止时完成,但这可能反过来触发其他依赖阶段。在这个接口中定义的功能只采用了几个基本形式,这些形式扩展为一组更大的方法,以捕捉一系列的使用风格

阶段执行的计算可以表示为Function、Consumer或Runnable(分别使用名称包括apply、accept或run的方法),这取决于它是否需要参数和/或产生结果。例如,舞台。thenApply (x - >平方(x))。thenAccept(x -> System.out.print(x)). thenrun (() -> System.out.println())。另一种形式(compose)应用阶段本身的函数,而不是它们的结果。 一个阶段的执行可能由一个阶段的完成,或两个阶段的两个都完成,或两个阶段中的任何一个完成而触发。单个阶段上的依赖关系使用带有前缀then的方法进行安排。通过完成这两个阶段而触发的可能会结合它们的结果或效果,使用相应命名的方法。由两个阶段中的任何一个触发的结果都不能保证在相关阶段的计算中使用哪个结果或效果。 阶段之间的依赖关系控制计算的触发,但不保证任何特定的顺序。此外,新阶段计算的执行可以以三种方式中的任何一种安排:默认执行、默认异步执行(使用使用阶段默认异步执行功能的后缀async的方法)或自定义(通过提供的Executor)。默认和异步模式的执行属性是由CompletionStage实现指定的,而不是这个接口。带有显式Executor参数的方法可能具有任意的执行属性,甚至可能不支持并发执行,但是以一种适应异步的方式安排处理。 无论触发阶段是否正常完成或异常完成,有两种方法形式支持处理:method whenComplete允许不管结果如何注入动作,否则在完成时保留结果。方法句柄还允许该阶段计算替换结果,从而允许其他依赖阶段进行进一步处理。在所有其他情况下,如果某个阶段的计算因一个(未检查的)异常或错误而突然终止,那么要求其完成的所有依赖阶段也会异常地完成,并将CompletionException作为其原因。如果一个阶段依赖于这两个阶段,并且都异常完成,那么CompletionException可以对应于这两个异常中的任何一个。如果一个阶段依赖于另外两个阶段中的任何一个,并且其中只有一个异常完成,则不能保证该依赖阶段正常完成还是异常完成。在方法whenComplete的情况下,当提供的操作本身遇到异常时,如果阶段还没有完成异常,则异常地用这个异常完成。 所有方法都遵守上述触发、执行和异常完成规范(在单个方法规范中不会重复)。此外,虽然用于为接受它们的方法传递完成结果(即类型为T的参数)的参数可能为空,但为任何其他参数传递空值都会导致抛出NullPointerException。 此接口不定义用于初始创建、强制正常或异常完成、探测完成状态或结果或等待阶段完成的方法。CompletionStage的实现可以适当地提供实现这些效果的方法。方法toCompletableFuture通过提供一个通用转换类型来实现此接口的不同实现之间的互操作性

详解

简单的任务,用Future获取结果还好,但我们并行提交的多个异步任务,往往并不是独立的,**很多时候业务逻辑处理存在串行[依赖]、并行、聚合的关系。**如果要我们手动用 Fueture实现,是非常麻烦的
**CompletableFuture是Future接口的扩展和增强。**CompletableFuture实现了Future接口,并在此基础上进行了丰富地扩展,完美地弥补了Future上述的种种问题。更为重要的是,CompletableFuture实现了对任务的编排能力。借助这项能力,我们可以轻松地组织不同任务的运行顺序、规则以及方式。从某种程度上说,这项能力是它的核心能力。而在以往,虽然通过CountDownLatch等工具类也可以实现任务的编排,但需要复杂的逻辑处理,不仅耗费精力且难以维护
默认使用
ForkJoinPool.commonPool()

应用场景

描述依赖关系

  • thenApply() 把前面异步任务的结果,交给后面的Function
  • thenCompose()用来连接两个有依赖关系的任务,结果由第二个任务返回

描述and聚合关系

  • thenCombine:任务合并,有返回值
  • thenAccepetBoth:两个任务执行完成后,将结果交给thenAccepetBoth消耗,无返回值
  • runAfterBoth:两个任务都执行完成后,执行下一步操作(Runnable)

描述or聚合关系

  • applyToEither:两个任务谁执行的快,就使用那一个结果,有返回值
  • acceptEither: 两个任务谁执行的快,就消耗那一个结果,无返回值
  • runAfterEither: 任意一个任务执行完成,进行下一步操作(Runnable)

并行执行

  • CompletableFuture类自己也提供了anyOf()和allOf()用于支持多个CompletableFuture并行执行

创建异步操作

CompletableFuture 提供了四个静态方法来创建一个异步操作:

public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

这四个方法区别在于:

  • runAsync 方法以Runnable函数式接口类型为参数,没有返回结果,supplyAsync方法Supplier函数式接口类型为参数,返回结果类型为U;Supplier 接口的 get() 方法是有返回值的(会阻塞)
  • **没有指定Executor的方法会使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。**如果指定线程池,则使用指定的线程池运行
  • 默认情况下 CompletableFuture 会使用公共的 ForkJoinPool 线程池,这个线程池默认创建的线程数是 CPU 的核数(也可以通过 JVM option:-Djava.util.concurrent.ForkJoinPool.common.parallelism 来设置 ForkJoinPool 线程池的线程数)。如果所有 CompletableFuture 共享一个线程池,那么一旦有任务执行一些很慢的 I/O 操作,就会导致线程池中所有线程都阻塞在 I/O 操作上,从而造成线程饥饿,进而影响整个系统的性能。所以,强烈建议你要根据不同的业务类型创建不同的线程池,以避免互相干扰

代码示例:

public class CompletableFutureDemo01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Runnable runnable = () -> System.out.println("执行无返回结果的异步任务");
        CompletableFuture.runAsync(runnable);
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("执行有返回值的异步任务");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Hello World";
        });
        String result = future.join();
        System.out.println(result);
    }
}

运行结果:

执行无返回结果的异步任务
执行有返回值的异步任务
Hello World

获取结果join&get

join()和get()方法都是用来获取CompletableFuture异步之后的返回值。 join()方法抛出的是uncheck异常(即未经检查的异常),不会强制开发者抛出。get()方法抛出的是经过检查的异常,ExecutionException, InterruptedException 需要用户手动处理(抛出或者 try catch)

结果处理

当CompletableFuture的计算结果完成,或者抛出异常的时候,我们可以执行特定的 Action。主要是下面的方法:

public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn)
  • action的类型是BiConsumer,它可以处理正常的计算结果,或者异常情况
  • 方法不以Async结尾,意味着Action使用相同的线程执行,而Async可能会使用其它的线程去执行(如果使用相同的线程池,也可能会被同一个线程选中执行)
  • 这几个方法都会返回CompletableFuture,当action执行完毕后它的结果返回原始的CompletableFuture的计算结果或者返回异常

代码示例:

public class CompletableFutureDemo02 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
            }
            if (new Random().nextInt(10) % 2 == 0) {
                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";
            }
        }).join();
    }
}

运行结果:

执行结束!
test 执行完成!
或者
执行失败:java.lang.ArithmeticException: / by zero
null 执行完成!

结果转换

所谓结果转换,就是将上一段任务的执行结果作为下一阶段任务的入参参与重新计算,产生新的结果

thenApply

thenApply 接收一个函数作为参数,使用该函数处理上一个CompletableFuture 调用的结果,并返回一个具有处理结果的Future对象

public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)

代码示例:

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            int result = 100;
            System.out.println("一阶段:" + result);
            return result;
        }).thenApply(number -> {
            int result = number * 3;
            System.out.println("二阶段:" + result);
            return result;
        });

        System.out.println("最终结果:" + future.get());
    }
}

运行结果:

一阶段:100
二阶段:300
最终结果:300

thenCompose

thenCompose 的参数为一个返回 CompletableFuture 实例的函数,该函数的参数是先前计算步骤的结果

public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn);
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn) ;
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn, Executor executor) ;

代码示例:

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> future = CompletableFuture
                .supplyAsync(() -> {
                    int number = new Random().nextInt(30);
                    System.out.println("第一阶段:" + number);
                    return number;
                })
                .thenCompose(param -> CompletableFuture.supplyAsync(() -> {
                    int number = param * 2;
                    System.out.println("第二阶段:" + number);
                    return number;
                }));
        System.out.println("最终结果: " + future.get());
    }
}

运行结果:

第一阶段:29
第二阶段:58
最终结果: 58

thenApply 和 thenCompose的区别

  • thenApply 转换的是泛型中的类型,返回的是同一个CompletableFuture
  • thenCompose 将内部的 CompletableFuture 调用展开来并使用上一个CompletableFutre 调用的结果在下一步的 CompletableFuture 调用中进行运算,是生成一个新的CompletableFuture

代码示例:

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
        CompletableFuture<String> result1 = future.thenApply(param -> param + " World");
        CompletableFuture<String> result2 = future.thenCompose(param -> CompletableFuture.supplyAsync(() -> param + "World"));
        System.out.println(result1.get());
        System.out.println(result2.get());
    }
}
//运行结果
Hello World
HelloWorld

结果消费

与结果处理和结果转换系列函数返回一个新的 CompletableFuture 不同,结果消费系列函数只对结果执行Action,而不返回新的计算值
根据对结果的处理方式,结果消费函数又分为:

  • thenAccept系列:对单个结果进行消费
  • thenAcceptBoth系列:对两个结果进行消费
  • thenRun系列:不关心结果,只对结果执行Action

thenAccept

通过观察该系列函数的参数类型可知,它们是函数式接口Consumer,这个接口只有输入,没有返回值

public CompletionStage<Void> thenAccept(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor);

代码示例:

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Void> future = CompletableFuture
                .supplyAsync(() -> {
                    int number = new Random().nextInt(10);
                    System.out.println("第一阶段:" + number);
                    return number;
                }).thenAccept(number ->
                        System.out.println("第二阶段:" + number * 5));

        System.out.println("最终结果:" + future.get());
    }
}

运行结果:

第一阶段:2
第二阶段:10
最终结果:null

thenAcceptBoth

thenAcceptBoth 函数的作用是,当两个 CompletionStage 都正常完成计算的时候,就会执行提供的action消费两个异步的结果

public <U> CompletionStage<Void> thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);
public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);
public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action, Executor executor);

代码示例:

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(3) + 1;
            try {
                TimeUnit.SECONDS.sleep(number);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("第一阶段:" + number);
            return number;
        });
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(3) + 1;
            try {
                TimeUnit.SECONDS.sleep(number);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("第二阶段:" + number);
            return number;
        });
        future1.thenAcceptBoth(future2, (x, y) -> System.out.println("最终结果:" + (x + y))).join();
    }
}

运行结果:

第一阶段:3
第二阶段:3
最终结果:6

thenRun

thenRun 也是对线程任务结果的一种消费函数,与thenAccept不同的是,thenRun 会在上一阶段 CompletableFuture 计算完成的时候执行一个Runnable,Runnable并不使用该CompletableFuture 计算的结果

public CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);

代码示例:

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(10);
            System.out.println("第一阶段:" + number);
            return number;
        }).thenRun(() ->
                System.out.println("thenRun 执行"));
        System.out.println("最终结果:" + future.get());
    }
}

运行结果:

第一阶段:1
thenRun 执行
最终结果:null

结果组合

thenCombine

thenCombine 方法,合并两个线程任务的结果,并进一步处理

public <U,V> CompletionStage<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn,Executor executor);

代码示例:

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> future1 = CompletableFuture
                .supplyAsync(() -> {
                    int number = new Random().nextInt(10);
                    System.out.println("第一阶段:" + number);
                    return number;
                });
        CompletableFuture<Integer> future2 = CompletableFuture
                .supplyAsync(() -> {
                    int number = new Random().nextInt(10);
                    System.out.println("第二阶段:" + number);
                    return number;
                });
        CompletableFuture<Integer> result = future1
                .thenCombine(future2, (x, y) -> x + y);
        System.out.println("最终结果:" + result.get());
    }
}

运行结果:

第一阶段:7
第二阶段:3
最终结果:10

任务交互

所谓线程交互,是指将两个线程任务获取结果的速度相比较,按一定的规则进行下一步处理

applyToEither

两个线程任务相比较,先获得执行结果的,就对该结果进行下一步的转化操作

public <U> CompletionStage<U> applyToEither(CompletionStage<? extends T> other,Function<? super T, U> fn);
public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,Function<? super T, U> fn);
public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,Function<? super T, U> fn,Executor executor);

代码示例:

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(10);
            System.out.println("第一阶段start:" + number);
            try {
                TimeUnit.SECONDS.sleep(number);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("第一阶段end:" + number);
            return number;
        });
        CompletableFuture<Integer> future2 = CompletableFuture
                .supplyAsync(() -> {
                    int number = new Random().nextInt(10);
                    System.out.println("第二阶段start:" + number);
                    try {
                        TimeUnit.SECONDS.sleep(number);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("第二阶段end:" + number);
                    return number;
                });

        future1.applyToEither(future2, number -> {
            System.out.println("最快结果:" + number);
            return number * 2;
        }).join();
    }
}

运行结果:

第一阶段start:0
第二阶段start:1
第一阶段end:0
最快结果:0

acceptEither

两个线程任务相比较,先获得执行结果的,就对该结果进行下一步的消费操作

public CompletionStage<Void> acceptEither(CompletionStage<? extends T> other,Consumer<? super T> action);
public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,Consumer<? super T> action);
public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,Consumer<? super T> action,Executor executor);

代码示例:

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(10) + 1;
            try {
                TimeUnit.SECONDS.sleep(number);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("第一阶段:" + number);
            return number;
        });

        CompletableFuture<Integer> future2 = CompletableFuture
                .supplyAsync(() -> {
                    int number = new Random().nextInt(10) + 1;
                    try {
                        TimeUnit.SECONDS.sleep(number);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("第二阶段:" + number);
                    return number;
                });

        future1.acceptEither(future2, number -> System.out.println("最快结果:" + number)).join();
    }
}

运行结果:

第二阶段:6
最快结果:6

runAfterEither

两个线程任务相比较,有任何一个执行完成,就进行下一步操作,不关心运行结果

public CompletionStage<Void> runAfterEither(CompletionStage<?> other,Runnable action);
public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action);
public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action,Executor executor);

代码示例:

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(5);
            try {
                TimeUnit.SECONDS.sleep(number);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("第一阶段:" + number);
            return number;
        });

        CompletableFuture<Integer> future2 = CompletableFuture
                .supplyAsync(() -> {
                    int number = new Random().nextInt(5);
                    try {
                        TimeUnit.SECONDS.sleep(number);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("第二阶段:" + number);
                    return number;
                });

        future1.runAfterEither(future2, () -> System.out.println("已经有一个任务完成了")).join();
    }
}

运行结果:

第二阶段:2
已经有一个任务完成了

runAfterBoth

两个线程任务相比较,两个全部执行完成,才进行下一步操作,不关心运行结果

public CompletionStage<Void> runAfterBoth(CompletionStage<?> other,Runnable action);
public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action);
public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action,Executor executor);

代码示例:

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> future1 = CompletableFuture
                .supplyAsync(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("第一阶段:1");
                    return 1;
                });

        CompletableFuture<Integer> future2 = CompletableFuture
                .supplyAsync(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("第二阶段:2");
                    return 2;
                });
        future1.runAfterBoth(future2, new Runnable() {
            @Override
            public void run() {
                System.out.println("上面两个任务都执行完成了。");
            }
        }).get();
    }
}

运行结果:

第一阶段:1
第二阶段:2
上面两个任务都执行完成了。

anyOf

anyOf 方法的参数是多个给定的 CompletableFuture,当其中的任何一个完成时,方法返回这个 CompletableFuture

public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)

代码示例:

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Random random = new Random();
        CompletableFuture<String> future1 = CompletableFuture
                .supplyAsync(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(random.nextInt(5));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return "hello";
                });

        CompletableFuture<String> future2 = CompletableFuture
                .supplyAsync(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(random.nextInt(1));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return "world";
                });
        CompletableFuture<Object> result = CompletableFuture.anyOf(future1, future2);
        System.out.println(result.get());
    }
}
//运行结果:hello或world

allOf

allOf方法用来实现多 CompletableFuture 的同时返回

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)

代码示例:

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> future1 = CompletableFuture
                .supplyAsync(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("future1完成!");
                    return "future1完成!";
                });

        CompletableFuture<String> future2 = CompletableFuture
                .supplyAsync(() -> {
                    System.out.println("future2完成!");
                    return "future2完成!";
                });

        CompletableFuture<Void> combindFuture = CompletableFuture
                .allOf(future1, future2);
        try {
            combindFuture.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("future1: " + future1.isDone() + ",future2: " + future2.isDone());
    }
}

运行结果:

future2完成!
future1完成!
future1: true,future2: true

CompletableFuture常用方法总结

在这里插入图片描述

使用示例

实现最优的“烧水泡茶”程序

著名数学家华罗庚先生在《统筹方法》这篇文章里介绍了一个烧水泡茶的例子,文中提到最优的工序应该是下面这样:

在这里插入图片描述


对于烧水泡茶这个程序,一种最优的分工方案:用两个线程 T1 和 T2 来完成烧水泡茶程序,T1 负责洗水壶、烧开水、泡茶这三道工序,T2 负责洗茶壶、洗茶杯、拿茶叶三道工序,其中T1 在执行泡茶这道工序时需要等待 T2 完成拿茶叶的工序

基于Future实现

@Slf4j
public class FutureTaskDemo3 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建任务T2的FutureTask
        FutureTask<String> ft2 = new FutureTask<>(new T2Task());
        // 创建任务T1的FutureTask
        FutureTask<String> ft1 = new FutureTask<>(new T1Task(ft2));
        // 线程T1执行任务ft1
        Thread T1 = new Thread(ft1);
        T1.start();
        // 线程T2执行任务ft2
        Thread T2 = new Thread(ft2);
        T2.start();
        // 等待线程T1执行结果
        log.info(ft1.get());
    }
}

// T1Task需要执行的任务:
// 洗水壶、烧开水、泡茶
@Slf4j
class T1Task implements Callable<String> {
    FutureTask<String> ft2;

    // T1任务需要T2任务的FutureTask
    T1Task(FutureTask<String> ft2) {
        this.ft2 = ft2;
    }

    @Override
    public String call() throws Exception {
        log.info("T1:洗水壶...");
        TimeUnit.SECONDS.sleep(1);

        log.info("T1:烧开水...");
        TimeUnit.SECONDS.sleep(15);
        // 获取T2线程的茶叶
        String tf = ft2.get();
        log.info("T1:拿到茶叶:" + tf);

        log.info("T1:泡茶...");
        return "上茶:" + tf;
    }
}

// T2Task需要执行的任务:
// 洗茶壶、洗茶杯、拿茶叶
@Slf4j
class T2Task implements Callable<String> {
    @Override
    public String call() throws Exception {
        log.info("T2:洗茶壶...");
        TimeUnit.SECONDS.sleep(1);

        log.info("T2:洗茶杯...");
        TimeUnit.SECONDS.sleep(2);

        log.info("T2:拿茶叶...");
        TimeUnit.SECONDS.sleep(1);
        return "龙井";
    }
}

运行结果:

18:31:21.151 [Thread-1] INFO com.example.demo.seven_two_six.concurrent.future.T2Task - T2:洗茶壶…
18:31:21.151 [Thread-0] INFO com.example.demo.seven_two_six.concurrent.future.T1Task - T1:洗水壶…
18:31:22.156 [Thread-1] INFO com.example.demo.seven_two_six.concurrent.future.T2Task - T2:洗茶杯…
18:31:22.156 [Thread-0] INFO com.example.demo.seven_two_six.concurrent.future.T1Task - T1:烧开水…
18:31:24.168 [Thread-1] INFO com.example.demo.seven_two_six.concurrent.future.T2Task - T2:拿茶叶…
18:31:37.164 [Thread-0] INFO com.example.demo.seven_two_six.concurrent.future.T1Task - T1:拿到茶叶:龙井
18:31:37.164 [Thread-0] INFO com.example.demo.seven_two_six.concurrent.future.T1Task - T1:泡茶…
18:31:37.164 [main] INFO com.example.demo.seven_two_six.concurrent.future.FutureTaskDemo3 - 上茶:龙井

基于CompletableFuture实现

@Slf4j
public class CompletableFutureDemo2 {
    public static void main(String[] args) {
        //任务1:洗水壶->烧开水
        CompletableFuture<Void> f1 = CompletableFuture
                .runAsync(() -> {
                    log.info("T1:洗水壶...");
                    sleep(1);

                    log.info("T1:烧开水...");
                    sleep(15);
                });
        //任务2:洗茶壶->洗茶杯->拿茶叶
        CompletableFuture<String> f2 = CompletableFuture
                .supplyAsync(() -> {
                    log.info("T2:洗茶壶...");
                    sleep(1);

                    log.info("T2:洗茶杯...");
                    sleep(2);

                    log.info("T2:拿茶叶...");
                    sleep(1);
                    return "龙井";
                });
        //任务3:任务1和任务2完成后执行:泡茶
        CompletableFuture<String> f3 = f1.thenCombine(f2, (__, tf) -> {
            log.info("T1:拿到茶叶:" + tf);
            log.info("T1:泡茶...");
            return "上茶:" + tf;
        });
        //等待任务3执行结果
        log.info(f3.join());
    }
    static void sleep(int t) {
        try {
            TimeUnit.SECONDS.sleep(t);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果:

18:33:18.774 [ForkJoinPool.commonPool-worker-2] INFO com.example.demo.seven_two_six.concurrent.future.CompletableFutureDemo2 - T2:洗茶壶…
18:33:18.774 [ForkJoinPool.commonPool-worker-9] INFO com.example.demo.seven_two_six.concurrent.future.CompletableFutureDemo2 - T1:洗水壶…
18:33:19.794 [ForkJoinPool.commonPool-worker-9] INFO com.example.demo.seven_two_six.concurrent.future.CompletableFutureDemo2 - T1:烧开水…
18:33:19.794 [ForkJoinPool.commonPool-worker-2] INFO com.example.demo.seven_two_six.concurrent.future.CompletableFutureDemo2 - T2:洗茶杯…
18:33:21.799 [ForkJoinPool.commonPool-worker-2] INFO com.example.demo.seven_two_six.concurrent.future.CompletableFutureDemo2 - T2:拿茶叶…
18:33:34.804 [ForkJoinPool.commonPool-worker-9] INFO com.example.demo.seven_two_six.concurrent.future.CompletableFutureDemo2 - T1:拿到茶叶:龙井
18:33:34.804 [ForkJoinPool.commonPool-worker-9] INFO com.example.demo.seven_two_six.concurrent.future.CompletableFutureDemo2 - T1:泡茶…
18:33:34.804 [main] INFO com.example.demo.seven_two_six.concurrent.future.CompletableFutureDemo2 - 上茶:龙井

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/106116.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Confluence 调整会话超时(session timeout)

文章目录前言一、概括二、实际场景应用1.更改空闲超时2.更改记住我 cookie 的生命周期3.在用户通过身份验证后的某个时间强制注销用户总结前言 在 Confluence 中有两个会话 Cookie&#xff1a; JSESSIONID: 由 Tomcat 使用和管理。 默认情况下&#xff0c;这被视为会话 cooki…

类与对象(中)

类与对象类的6个默认成员函数构造函数概念特性析构函数概念特性拷贝构造函数概念特性赋值运算符重载运算符重载赋值运算符重载前置 后置 重载const成员函数取地址及const取地址操作符重载类的6个默认成员函数 当类中没有任何成员时&#xff0c;称作空类 但是呢&#xff0c;编译…

Docker使用(容器、镜像相关命令)

虚拟化 在计算机中&#xff0c;虚拟化&#xff08;英语&#xff1a;Virtualization&#xff09;是一种资源管理技术&#xff0c;是将计算机的各种实体资源&#xff0c;如服务器、网络、内存及存储等&#xff0c;予以抽象、转换后呈现出来&#xff0c;打破实体结构间的不可切割…

虹科方案|将以太网连接添加到Dell EMC PowerVault™ ML3 SAS库

一、Dell EMC 和 ATTO 磁带解决方案 Dell EMC 和 ATTO 提供了业界唯一的商用解决方案&#xff0c;可将高速以太网连接添加 到标准 SAS LTO 磁带驱动器。ATTO XstreamCORE ET 8200 智能网桥允许您使用 iSCSI 和 iSER 协议通过标准以太网远程连接到 SAS 磁带驱动器。当与采用最新…

花 2 个月备战字节跳动Java岗,3 轮面试拿下 60W Offer

最近收到一位刚入职字节的 Java 工程师朋友投稿——以下内容来自其亲身经历&#xff0c;某双非硕士拿到 字节 60W offer &#xff0c;感谢他的走心分享&#xff08;文末附硬货笔记&#xff09; PART1&#xff1a;个人情况简介 菜 J 一枚&#xff0c;本硕都是计算机&#xff08…

[论文阅读] SqueezeSeg V1

文章目录1. 主要思想2. 具体方法2.1 数据处理方式2.2 网络架构3. 实验支撑4. 总结启示5. 相关文献paper 原论文的链接 code: 源代码链接 paper全称&#xff1a;SqueezeSeg: Convolutional Neural Nets with Recurrent CRF for Real-Time Road-Object Segmentation from 3D LiDA…

【02】FreeRTOS获取10.4.6源码+移植到STM32F407步骤

目录 1.获取FreeRTOS源码 1.1 FreeRTOS官网下载步骤 1.2FreeRTOS源码内容 1.3FreeRTOS内核文件 1.3.1Demo文件夹 1.3.2Source文件夹 2.FreeRTOS移植 2.1添加FreeRTOS源码 2.1.1复制FreeRTOS源码 2.1.2将文件添加到工程 2.1.3添加头文件路径 2.2添加FreeRTOS.h 2.3修改SYS…

vpp process类型节点调度过程

vpp节点类型 VLIB_NODE_TYPE_PROCESS&#xff1a;process类型节点可以被挂起也可以被恢复&#xff0c;main线程上调度 &#xff08;免费订阅,永久学习&#xff09;学习地址: Dpdk/网络协议栈/vpp/OvS/DDos/NFV/虚拟化/高性能专家-学习视频教程-腾讯课堂 process节点注册 pro…

【MC】新加载器 Quilt 好用吗?和 Fabric 相比好在哪?

在今年四月 (2022/4/20) &#xff0c;一个船新加载器 Quilt 发布了第一个测试版。 Quilt officially entered its first beta today, attracting an influx of new users and an amazing amount of support and positive feedback. By the end of the day, Quilt was happily l…

Go语言设计与实现 -- 字符串

Go语言的字符串与Java和python是一样的。具有不可变性。是一个只读的字节数组&#xff0c;如图所示。 因为Go的字符串具有不可变性&#xff0c;所以我们只能通过string和[]byte类型之间反复转换实现修改。 将这一段内存复制到栈上将变量的类型转换成[]byte后并修改字节数据将修…

功能上新|使用 Excel 低门槛进行指标分析!

Kyligence Zen 功能上新啦&#xff01;用户不仅可以在 Kyligence Zen 中定义、分析和管理指标&#xff0c;还可直接使用 Excel 插件来分析 Kyligence Zen 中已经定义好的指标&#xff0c;学习无门槛&#xff0c;上手更轻松&#xff01;欢迎访问 http://zen.kyligence.io 申请免…

实验二A 图像的空域(源代码一站式复制粘贴)

实验二A 图像的空域一、实验目的二、实验原理三、实验内容与要求四、实验的具体实现一、实验目的 1.掌握图像滤波的基本定义及目的。 2.理解空间域滤波的基本原理及方法。 3.掌握进行图像的空域滤波的方法。 二、实验原理 1.空域增强 空域滤波是在图像空间中借助模板对图像进…

阳哥JUC并发编程之AQS后篇全网最详细源码笔记

文章目录AQS后序课程笔记AQS源码ReentryLock锁的原理分析公平锁以及非公平锁源码详解Aquire方法调用原码流程分析第一步、tryAquire第二步、addwrite第三步&#xff1a;aquireQueuedAQS释放锁的过程第一步、释放锁第二步进入aquireQueueAQS异常情况下走Cancel流程分析第一种队尾…

ECharts项目实战:全球GDP数据可视化

【课程简介】 可视化是前端里一个几乎可以不用写网页&#xff0c;但又发展得非常好的方向。在互联网产品里&#xff0c;无论是C端中常见的双十一购物节可视化大屏&#xff0c;还是B端的企业中后台管理系统都离不开可视化。国家大力推动的智慧城市、智慧社区中也有很多可视化的…

对于DDoS攻击防御有哪些误区?

​  DDoS攻击是属于常见网络攻击之一&#xff0c;也是一种较难防御的网络攻击。它的特点就是易于启动、难防御、有害、难跟踪等。因此DDoS攻击也是众多站长最怕遇见的网络攻击。那么大家在使用海外服务器时&#xff0c;会有哪些DDoS攻击防御的误区呢? 1、防御全部DDoS攻击 防…

CSS -- 09. 移动WEB开发之flex布局

文章目录移动WEB开发之flex布局1 flex布局原理2 常见的父项属性2.1 设置主轴方向 flex-direction2.2 设置主轴上的子元素排列方式 justify-content2.3 设置元素是否换行 flex-wrap2.4 设置侧轴上的子元素的排列方式&#xff08;单行&#xff09; align-items2.5 设置侧轴上的子…

【矩阵论】6.范数理论——基本概念——矩阵范数生成向量范数谱范不等式

6.1.3 矩阵范数产生向量范数 CnnC^{n\times n}Cnn 上任一矩阵范数 ∥∙∥\Vert \bullet\Vert∥∙∥ 都产生一个向量范数 φ(X)∥X∥V\varphi(X)\Vert X\Vert_Vφ(X)∥X∥V​ 矩阵范数与向量范数的相容性&#xff1a;φ(Ax)≤∥A∥φ(x)\varphi(Ax)\le \Vert A\Vert\varphi(x)φ…

MySQL SSL安全解读

安全一直是不可不重视的问题。目前MySQL这方面应大方向上技术手段都具备。如&#xff1a;网络链接&#xff0c;权限控制&#xff0c;key秘钥认证&#xff0c;数据加密脱敏 等方式。综合考虑&#xff0c;虽然很多环境无法所有这些安全策略全部应用上&#xff0c;但在可控范围内尽…

【C语言数据结构(基础版)】第五站:树和二叉树

目录 一、树的概念及结构 1.树的概念 2.树的表示 3.树在实际中的应用 二、二叉树概念及结构 1.概念 2.特殊的二叉树 3.二叉树的性质 4.二叉树的存储结构 &#xff08;1&#xff09;顺序存储 &#xff08;2&#xff09;链式存储 三、二叉树链式结构的实现 1.二叉树的…

Mysql的MVCC知多少(隐藏字段,undolog版本链和ReadView)

文章目录前言一、什么是MVCC二、快照读与当前读三、4种隔离级别与MVCC四、MVCC实现4.1 隐藏字段和undolog版本链4.2 ReadView五、说明前言 其实自己之前对MVCC知之甚少&#xff0c;总觉得是一块很难啃的骨头&#xff0c;有点内惧&#xff0c;但当你真的掌握之后&#xff0c;就…