java同步器

1、概述

        JUC提供了一些工具类,实现了一些常见的线程间同步模式,常用同步器如下

  • CountDownLatch : 计数递减门闩,用于控制一个线程等待其他线程都到达指定位置时才开始工作。
  • CyclicBarrier : 循环栅栏,用于控制多个线程在一些指定的位置同步,可以指定多个位置。
  • Phaser : 动态阶段同步器,相当于增强的CyclicBarrier和CountDownLatch。
  • Semaphore : 信号量,用于控制线程同步。
  • Exchanger : 交换器,用于两个线程交换数据。
  • CompletableFuture : 多任务异步处理工具。

2、Semaphore 同步器

经典的信号量,通过计数器控制对共享资源的访问。

方法

  • Semaphore(int count): 创建拥有 count 个许可证的信号量
  • acquire()/acquire(int num) :  获取 1/num 个许可证
  • release/release(int num) :  释放 1/num 个许可证

代码demo

package com.ybw.jdk8.demo.synchronizer;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Semaphore;

/**
 * 同步器-信号量
 *
 * @author weixiansheng
 * @version V1.0
 * @className SemaphoreDemo
 * @date 2023/11/15
 **/
@Slf4j
public class SemaphoreDemo {

    /**
     * 这段代码演示了如何使用java.util.concurrent.Semaphore来控制同时访问某个资源的线程数量。
     * 在这个例子中,Semaphore对象被初始化为具有2个许可证的数量。每个线程在执行任务之前都会获取许可证,
     * 然后释放许可证以便其他线程可以获取。当所有许可证都被占用时,尝试获取许可证的线程将会被阻塞,直到有其他线程释放许可证。
     * 这个例子中,两个线程每次运行1秒,因此每次只有一个线程运行,总共运行10秒。
     *
     * @param args
     * @methodName: main
     * @return: void
     * @author: weixiansheng
     * @date: 2023/11/15
     **/
    public static void main(String[] args) throws InterruptedException {
        Semaphore semaphore = new Semaphore(2);

        Thread t1 = new Thread(() -> {
            try {
                for (int i = 0; i < 5; i++) {
                    semaphore.acquire();
                    log.info("Thread 1: Running");
                    Thread.sleep(1000);
                    semaphore.release();
                }
            } catch (InterruptedException e) {
                log.error("Thread 1: Interrupted");
            }
        });

        Thread t2 = new Thread(() -> {
            try {
                for (int i = 0; i < 5; i++) {
                    semaphore.acquire();
                    log.info("Thread 2: Running");
                    Thread.sleep(1000);
                    semaphore.release();
                }
            } catch (InterruptedException e) {
                log.error("Thread 2: Interrupted");
            }
        });

        t1.start();
        t2.start();
    }
}

        这段代码演示了如何使用java.util.concurrent.Semaphore来控制同时访问某个资源的线程数量。在这个例子中,Semaphore对象被初始化为具有2个许可证的数量。每个线程在执行任务之前都会获取许可证,然后释放许可证以便其他线程可以获取。当所有许可证都被占用时,尝试获取许可证的线程将会被阻塞,直到有其他线程释放许可证。这个例子中,两个线程每次运行1秒,因此每次只有一个线程运行,总共运行10秒。

打印日志如下

[INFO ] 2023-11-15 16:53:54.887 [Thread-1] c.y.j.d.synchronizer.SemaphoreDemo - Thread 2: Running
[INFO ] 2023-11-15 16:53:54.887 [Thread-0] c.y.j.d.synchronizer.SemaphoreDemo - Thread 1: Running
[INFO ] 2023-11-15 16:53:55.923 [Thread-0] c.y.j.d.synchronizer.SemaphoreDemo - Thread 1: Running
[INFO ] 2023-11-15 16:53:55.923 [Thread-1] c.y.j.d.synchronizer.SemaphoreDemo - Thread 2: Running
[INFO ] 2023-11-15 16:53:56.933 [Thread-0] c.y.j.d.synchronizer.SemaphoreDemo - Thread 1: Running
[INFO ] 2023-11-15 16:53:56.933 [Thread-1] c.y.j.d.synchronizer.SemaphoreDemo - Thread 2: Running
[INFO ] 2023-11-15 16:53:57.943 [Thread-0] c.y.j.d.synchronizer.SemaphoreDemo - Thread 1: Running
[INFO ] 2023-11-15 16:53:57.943 [Thread-1] c.y.j.d.synchronizer.SemaphoreDemo - Thread 2: Running
[INFO ] 2023-11-15 16:53:58.957 [Thread-1] c.y.j.d.synchronizer.SemaphoreDemo - Thread 2: Running
[INFO ] 2023-11-15 16:53:58.957 [Thread-0] c.y.j.d.synchronizer.SemaphoreDemo - Thread 1: Running

3、CountDownLatch 同步器

特征:
必须发生指定数量的事件后才可以继续运行 ( 比如赛跑比赛,裁判喊出 3,2,1 之后大家才同时跑 )

方法:

  • CountDownLatch(int count): 必须发生 count 个数量才可以打开锁存器
  • await: 等待锁存器,
    • 如果想让某个线程处于等待中,该线程调用countdownLatch.await()。
    • 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
  • countDown: 触发事件
    • 通过其他线程调用countdownLatch.countDown()减少计数器,直到减少到0,被await()挂起的线程恢复执行。

代码demo

package com.ybw.jdk8.demo.synchronizer;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;

/**
 * CountDownLatch 同步器
 *
 * @author weixiansheng
 * @version V1.0
 * @className CountDownLatchDemo
 * @date 2023/11/15
 **/
@Slf4j
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(10);

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                log.info("Thread " + Thread.currentThread().getId() + " is running.");
                log.info("Thread " + Thread.currentThread().getId() + " is finished.");
                //递减锁存器的计数
                latch.countDown();
            }).start();
        }

        log.info("All threads have started.");
        // 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
        latch.await();
        log.info("All threads have ended.");
    }
}

日志如下

[INFO ] 2023-11-15 17:11:52.589 [main] c.y.j.d.s.CountDownLatchDemo - All threads have started.
[INFO ] 2023-11-15 17:11:52.589 [Thread-2] c.y.j.d.s.CountDownLatchDemo - Thread 24 is running.
[INFO ] 2023-11-15 17:11:52.589 [Thread-3] c.y.j.d.s.CountDownLatchDemo - Thread 25 is running.
[INFO ] 2023-11-15 17:11:52.589 [Thread-5] c.y.j.d.s.CountDownLatchDemo - Thread 27 is running.
[INFO ] 2023-11-15 17:11:52.589 [Thread-8] c.y.j.d.s.CountDownLatchDemo - Thread 30 is running.
[INFO ] 2023-11-15 17:11:52.589 [Thread-1] c.y.j.d.s.CountDownLatchDemo - Thread 23 is running.
[INFO ] 2023-11-15 17:11:52.589 [Thread-7] c.y.j.d.s.CountDownLatchDemo - Thread 29 is running.
[INFO ] 2023-11-15 17:11:52.589 [Thread-0] c.y.j.d.s.CountDownLatchDemo - Thread 22 is running.
[INFO ] 2023-11-15 17:11:52.589 [Thread-4] c.y.j.d.s.CountDownLatchDemo - Thread 26 is running.
[INFO ] 2023-11-15 17:11:52.589 [Thread-6] c.y.j.d.s.CountDownLatchDemo - Thread 28 is running.
[INFO ] 2023-11-15 17:11:52.589 [Thread-9] c.y.j.d.s.CountDownLatchDemo - Thread 31 is running.
[INFO ] 2023-11-15 17:11:52.593 [Thread-3] c.y.j.d.s.CountDownLatchDemo - Thread 25 is finished.
[INFO ] 2023-11-15 17:11:52.593 [Thread-7] c.y.j.d.s.CountDownLatchDemo - Thread 29 is finished.
[INFO ] 2023-11-15 17:11:52.593 [Thread-2] c.y.j.d.s.CountDownLatchDemo - Thread 24 is finished.
[INFO ] 2023-11-15 17:11:52.593 [Thread-0] c.y.j.d.s.CountDownLatchDemo - Thread 22 is finished.
[INFO ] 2023-11-15 17:11:52.593 [Thread-1] c.y.j.d.s.CountDownLatchDemo - Thread 23 is finished.
[INFO ] 2023-11-15 17:11:52.593 [Thread-8] c.y.j.d.s.CountDownLatchDemo - Thread 30 is finished.
[INFO ] 2023-11-15 17:11:52.593 [Thread-6] c.y.j.d.s.CountDownLatchDemo - Thread 28 is finished.
[INFO ] 2023-11-15 17:11:52.593 [Thread-5] c.y.j.d.s.CountDownLatchDemo - Thread 27 is finished.
[INFO ] 2023-11-15 17:11:52.593 [Thread-4] c.y.j.d.s.CountDownLatchDemo - Thread 26 is finished.
[INFO ] 2023-11-15 17:11:52.593 [Thread-9] c.y.j.d.s.CountDownLatchDemo - Thread 31 is finished.
[INFO ] 2023-11-15 17:11:52.593 [main] c.y.j.d.s.CountDownLatchDemo - All threads have ended.

4、CyclicBarrier同步器

       翻译为“可循环利用的屏障”。它是一个同步的工具, 能够允许一组线程去互相等待直到都到达了屏障,CyclicBarrier对于涉及到固定大小的线程是非常有用的,线程们必须相互等待。该屏障称之为循环屏障,是因为当等待屏障的线程被释放之后,该屏障能循环使用。

特征:

适用于只有多个线程都到达预定点时才可以继续执行 ( 比如斗地主,需要等齐三个人才开始 )。

方法:

  • CyclicBarrier(int num) : 等待线程的数量
  • CyclicBarrier(int num, Runnable action) : 等待线程的数量以及所有线程到达后的操作
  • await() :  到达临界点后暂停线程

适用场景:

        有若干个线程,比如说有N个线程,需要它们都到达了某一个点之后才能开始一起执行,也就是说假如其中只有N-1个线程到达了这个点,还差一个线程没到达,此时这N-1个线程都会进入等待状态,直到第N个线程也到达了这个点之后,这N个线程才开始一起进行执行状态,这个场景跟CountDownLatch很类似。

代码demo

package com.ybw.jdk8.demo.synchronizer;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;

/**
 * CyclicBarrier 同步器
 *
 * @author weixiansheng
 * @version V1.0
 * @className CyclicBarrierDemo
 * @date 2023/11/15
 **/
@Slf4j
public class CyclicBarrierDemo {
    public static void main(String[] args) {
        // 创建一个线程池
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(20);
        int corePoolSize = 20;
        int maximumPoolSize = 30;
        long keepAliveTime = 60L;
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);

        // 创建一个屏障
        // parties:屏障解除之前必须调用await方法的线程数
        // barrierAction:屏障解除时执行的动作
        CyclicBarrier cyclicBarrier = new CyclicBarrier(10, () -> {
            log.info(Thread.currentThread().getName() + "已经到达检查点");
        });


        for (int i = 0; i < 10; i++) {
            threadPoolExecutor.submit(() -> {
                try {
                    log.info(Thread.currentThread().getName() + "已经到达屏障");
                    cyclicBarrier.await();
                    log.info(Thread.currentThread().getName() + "已经冲破屏障");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
        threadPoolExecutor.shutdown();

    }
}

日志如下

[INFO ] 2023-11-16 19:05:15.852 [pool-1-thread-5] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-5已经到达屏障
[INFO ] 2023-11-16 19:05:15.853 [pool-1-thread-4] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-4已经到达屏障
[INFO ] 2023-11-16 19:05:15.852 [pool-1-thread-7] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-7已经到达屏障
[INFO ] 2023-11-16 19:05:15.852 [pool-1-thread-10] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-10已经到达屏障
[INFO ] 2023-11-16 19:05:15.852 [pool-1-thread-3] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-3已经到达屏障
[INFO ] 2023-11-16 19:05:15.852 [pool-1-thread-9] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-9已经到达屏障
[INFO ] 2023-11-16 19:05:15.853 [pool-1-thread-1] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-1已经到达屏障
[INFO ] 2023-11-16 19:05:15.852 [pool-1-thread-6] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-6已经到达屏障
[INFO ] 2023-11-16 19:05:15.853 [pool-1-thread-2] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-2已经到达屏障
[INFO ] 2023-11-16 19:05:15.852 [pool-1-thread-8] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-8已经到达屏障
[INFO ] 2023-11-16 19:05:15.860 [pool-1-thread-2] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-2已经到达检查点
[INFO ] 2023-11-16 19:05:15.860 [pool-1-thread-2] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-2已经冲破屏障
[INFO ] 2023-11-16 19:05:15.860 [pool-1-thread-9] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-9已经冲破屏障
[INFO ] 2023-11-16 19:05:15.860 [pool-1-thread-5] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-5已经冲破屏障
[INFO ] 2023-11-16 19:05:15.860 [pool-1-thread-10] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-10已经冲破屏障
[INFO ] 2023-11-16 19:05:15.860 [pool-1-thread-4] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-4已经冲破屏障
[INFO ] 2023-11-16 19:05:15.860 [pool-1-thread-7] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-7已经冲破屏障
[INFO ] 2023-11-16 19:05:15.861 [pool-1-thread-6] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-6已经冲破屏障
[INFO ] 2023-11-16 19:05:15.861 [pool-1-thread-1] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-1已经冲破屏障
[INFO ] 2023-11-16 19:05:15.861 [pool-1-thread-3] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-3已经冲破屏障
[INFO ] 2023-11-16 19:05:15.861 [pool-1-thread-8] c.y.j.d.s.CyclicBarrierDemo - pool-1-thread-8已经冲破屏障

5、CompletableFuture

         简单的任务,用Future获取结果还好,但我们并行提交的多个异步任务,往往并不是独立的,很多时候业务逻辑处理存在串行[依赖]、并行、聚合的关系。如果要我们手动用 Fueture 实现,是非常麻烦的。

        CompletableFuture是Future接口的扩展和增强。CompletableFuture实现了Future接口,并在此基础上进行了丰富地扩展,完美地弥补了Future上述的种种问题。

        更为重要的是,CompletableFuture实现了对任务的编排能力。借助这项能力,我们可以轻松地组织不同任务的运行顺序、规则以及方式。从某种程度上说,这项能力是它的核心能力。而在以往,虽然通过CountDownLatch等工具类也可以实现任务的编排,但需要复杂的逻辑处理,不仅耗费精力且难以维护。

  • CompletionStage接口: 执行某一个阶段,可向下执行后续阶段。异步执行,默认线程池是ForkJoinPool.comcommonPool()。

方法思维导图

方法总结

  • 描述依赖关系
    • thenApply() 把前面异步任务的结果,交给后面的Function
    • thenCompose()用来连接两个有依赖关系的任务,结果由第二个任务返回
  • 描述and聚合关系
    • thenCombine 任务合并,有返回值
    • thenAccepetBoth 两个任务执行完成后,将结果交给thenAccepetBoth消耗,无返回值
    • runAfterBoth 两个任务都执行完成后,执行下一步操作(Runnable)
  • 描述or聚合关系
    • applyToEither 两个任务谁执行的快,就使用那一个结果,有返回值
    • acceptEither 两个任务谁执行的快,就消耗那一个结果,无返回值
    • runAfterEither 任意一个任务执行完成,进行下一步操作(Runnable)
  • 并行执行
    • CompletableFuture类自己也提供了anyOf()和allOf()用于支持多个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)
  • supply开头:这种方法,可以返回异步线程执行之后的结果。
  • run开头:这种不会返回结果,就只是执行线程任务。

        如果你想异步运行一些后台任务并且不想从任务中返回任何东西,那么你可以使用run开头的。

示例代码

package com.ybw.jdk8.demo.synchronizer;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * @author weixiansheng
 * @version V1.0
 * @className CompletableFutureDemo
 * @date 2023/11/16
 **/
@Slf4j
public class CompletableFutureDemo {
    public static void main(String[] args) throws InterruptedException {
        //1、执行无返回结果的异步任务
        CompletableFuture.runAsync(() -> log.info("执行无返回结果的异步任务"));
        //2、执行有返回值的异步任务
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            log.info("执行有返回值的异步任务");
            return "hello";
        });
        //3、获取异步任务的执行结果
        // t和u,代表任务的执行结果和异常
        future.whenComplete((t, u) -> {
            // 任务执行完成后,执行回调
            log.info("异步任务执行完成:{}", t);
        });
        TimeUnit.DAYS.sleep(1);
    }
}

打印日志如下

[INFO ] 2023-11-16 19:44:57.515 [ForkJoinPool.commonPool-worker-9] c.y.j.d.s.CompletableFutureDemo - 执行无返回结果的异步任务
[INFO ] 2023-11-16 19:44:57.515 [ForkJoinPool.commonPool-worker-2] c.y.j.d.s.CompletableFutureDemo - 执行有返回值的异步任务
[INFO ] 2023-11-16 19:44:57.520 [ForkJoinPool.commonPool-worker-2] c.y.j.d.s.CompletableFutureDemo - 异步任务执行完成:hello

thenApply()方法

        拿到上一个异步线程返回的结构进行后续处理。

示例代码

package com.ybw.jdk8.demo.synchronizer;

import lombok.extern.slf4j.Slf4j;

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

/**
 * @author weixiansheng
 * @version V1.0
 * @className CompletableFutureDemo
 * @date 2023/11/16
 **/
@Slf4j
public class CompletableFutureDemo {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            try {
                log.info("业务开始");
                // 模拟业务时长
                TimeUnit.SECONDS.sleep(1);
                log.info("业务结束");
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            return "商品信息";
        });

        CompletableFuture<String> resultFuture = future.thenApply(name -> "订单信息 " + name);
        log.info(resultFuture.get());


        TimeUnit.DAYS.sleep(1);
    }
}

打印日志

[INFO ] 2023-11-16 19:52:31.168 [ForkJoinPool.commonPool-worker-9] c.y.j.d.s.CompletableFutureDemo - 业务开始
[INFO ] 2023-11-16 19:52:32.183 [ForkJoinPool.commonPool-worker-9] c.y.j.d.s.CompletableFutureDemo - 业务结束
[INFO ] 2023-11-16 19:52:32.184 [main] c.y.j.d.s.CompletableFutureDemo - 订单信息 商品信息

thenAccept() 和 thenRun()方法

        如果你不想从你的回调函数中返回任何东西,只想在 Future 完成后运行一些代码,那么可以使用thenAccept()andthenRun()方法。这些方法是消费者Consumer<? super T> action,通常用作回调链中的最后一个回调。

  • thenAccept()可以拿到上一次的结果,然后执行。
  • thenRun()无法拿到上一次的结果,thenRun()意为然后执行什么,里面接收一个Runnable参数。
  • thenAccept、thenRun都是主线程执行的任务,顺序执行。

示例代码

package com.ybw.jdk8.demo.synchronizer;

import lombok.extern.slf4j.Slf4j;

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

/**
 * @author weixiansheng
 * @version V1.0
 * @className CompletableFutureDemo
 * @date 2023/11/16
 **/
@Slf4j
public class CompletableFutureDemo {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "商品信息");
        future.thenAccept(product  -> log.info("我拿到了 " + product));
        future.thenRun(()->{
            log.info("我执行完成了");
        });
        log.info("main");

        TimeUnit.DAYS.sleep(1);
    }
}

打印日志

[INFO ] 2023-11-16 20:01:28.468 [main] c.y.j.d.s.CompletableFutureDemo - 我拿到了 商品信息
[INFO ] 2023-11-16 20:01:28.471 [main] c.y.j.d.s.CompletableFutureDemo - 我执行完成了
[INFO ] 2023-11-16 20:01:28.471 [main] c.y.j.d.s.CompletableFutureDemo - main

complete()

        注意complete()其实也是个消费操作,但是与thenRun()不同的是,里面可以可抛出的异常

// 区别就是不是异步处理
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)

示例代码

package com.ybw.jdk8.demo.synchronizer;

import lombok.extern.slf4j.Slf4j;

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

/**
 * @author weixiansheng
 * @version V1.0
 * @className CompletableFutureDemo
 * @date 2023/11/16
 **/
@Slf4j
public class CompletableFutureDemo {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // 异步执行
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            int a = 10 / 0;
            log.info("执行结束!");
            return "hello";
        });
        // 完成时执行
        future.whenComplete((t, action) -> log.info(t + " 执行完成!"));

        // 异常时执行
        future.exceptionally(t -> {
            log.info("执行失败:" + t.getMessage());
            return "异常";
        }).join();

        TimeUnit.DAYS.sleep(1);
    }
}

日志打印

[INFO ] 2023-11-16 20:15:24.009 [main] c.y.j.d.s.CompletableFutureDemo - null 执行完成!
[INFO ] 2023-11-16 20:15:24.012 [main] c.y.j.d.s.CompletableFutureDemo - 执行失败:java.lang.ArithmeticException: / by zero

handle()

public <U> CompletableFuture<U> handle(BiFunction<? super T,Throwable,? extends U> fn)
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T,Throwable,? extends U> fn)
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T,Throwable,? extends U> fn, Executor executor)

        handle方法集和上面的complete方法集没有区别,同样有两个参数一个返回结果和可抛出异常,区别就在于返回值。

CompletableFuture 组合

示例代码

package com.ybw.jdk8.demo.synchronizer;

import lombok.extern.slf4j.Slf4j;

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

/**
 * @author weixiansheng
 * @version V1.0
 * @className CompletableFutureDemo
 * @date 2023/11/16
 **/
@Slf4j
public class CompletableFutureDemo {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        String result = CompletableFuture.supplyAsync(() -> "商品信息")
                .thenApply(product -> product + " 用户信息").join();
        log.info(result);

        TimeUnit.DAYS.sleep(1);
    }
}

日志打印

[INFO ] 2023-11-16 20:21:46.542 [main] c.y.j.d.s.CompletableFutureDemo - 商品信息 用户信息

多个 CompletableFuture 组合在一起

        假设有3个接口 获取用户信息,获取商品信息,获取会员信息,先每个接口1s,那个3个接口总耗时3s。按顺序执行此操作,但这将花费大量时间,所以异步线程会大大减少耗时,增加程序的性能。

CompletableFuture.allOf()

        CompletableFuture.allOf()里面包含了多个CompletableFuture操作,接收一个CompletableFuture类型的可变参数。

package com.ybw.jdk8.demo.synchronizer;

import lombok.extern.slf4j.Slf4j;

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

/**
 * @author weixiansheng
 * @version V1.0
 * @className CompletableFutureDemo
 * @date 2023/11/16
 **/
@Slf4j
public class CompletableFutureDemo {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        log.info("任务开始");
        // 异步执行1
        CompletableFuture<String> future1 = CompletableFuture
                .supplyAsync(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    log.info("future1完成!");
                    return "future1完成!";
                });
        // 异步执行2
        CompletableFuture<String> future2 = CompletableFuture
                .supplyAsync(() -> {
                    log.info("future2完成!");
                    return "future2完成!";
                });
        // 等待两个任务都完成
        CompletableFuture<Void> combindFuture = CompletableFuture
                .allOf(future1, future2);
        try {
            // 等待所有任务都完成
            combindFuture.get();
        } catch (Exception e) {
            log.error("异常", e);
        }
        log.info("任务结束");

        TimeUnit.DAYS.sleep(1);
    }
}

日志打印

[INFO ] 2023-11-17 09:49:58.601 [main] c.y.j.d.s.CompletableFutureDemo - 任务开始
[INFO ] 2023-11-17 09:49:58.639 [ForkJoinPool.commonPool-worker-2] c.y.j.d.s.CompletableFutureDemo - future2完成!
[INFO ] 2023-11-17 09:50:00.644 [ForkJoinPool.commonPool-worker-9] c.y.j.d.s.CompletableFutureDemo - future1完成!
[INFO ] 2023-11-17 09:50:00.644 [main] c.y.j.d.s.CompletableFutureDemo - 任务结束

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值