java异步与线程池

Java线程池

几种线程池简介

Executors.newFixedThreadPool

创建⼀个固定⼤⼩的线程池,可控制并发的线程数,超出的线程会在队列中等待;

Executors.newCachedThreadPool

创建⼀个可缓存的线程池,若线程数超过处理所需,缓存⼀段时间后会回收,若线程数不够,则新建线程;

Executors.newSingleThreadExecutor

创建单个线程数的线程池,它可以保证先进先出的执⾏顺序;

Executors.newScheduledThreadPool

创建⼀个可以执⾏延迟任务的线程池;

scheduleAtFixedRate

是以period为间隔来执行任务的,如果任务执行时间小于period,则上次任务执行完成后会间隔period后再去执行下一次任务;但如果任务执行时间大于period,则上次任务执行完毕后会不间隔的立即开始下次任务

scheduleWithFixedDelay 是不管任务执行多久,都会等上一次任务执行完毕后再延迟delay后去执行下次任务

Executors.newSingleThreadScheduledExecutor

创建⼀个单线程的可以执⾏延迟任务的线程池

Executors.newWorkStealingPool

创建⼀个抢占式执⾏的线程池(任务执⾏顺序不确定)【JDK1.8 添加】。

ThreadPoolExecutor

最原始的创建线程池的⽅式,它包含了 7 个参数可供设置。

ThreadPoolExecutor线程池

ThreadPoolExecutor是Java中最灵活、最常用的线程池。

ThreadPoolExecutor是一个灵活的线程池实现,可以通过调整参数来满足不同的需求,例如设置核心线程数、最大线程数、等待队列、拒绝策略等。

ThreadPoolExecutor还提供了预定义的线程池,例如Executors.newFixedThreadPoolExecutors.newCachedThreadPool等,这些预定义线程池都是基于ThreadPoolExecutor实现的,但是它们的参数是固定的,比较适合一些特定的场景。因此,ThreadPoolExecutor是Java中最灵活、最常用的线程池实现。

ThreadPoolExecutor是Java中线程池的实现类,其构造方法有7个参数,分别为:

  1. corePoolSize:线程池中的核心线程数,即线程池中始终保持的线程数,即使它们处于空闲状态也不会被销毁。如果提交的任务数小于corePoolSize,则线程池会创建新线程来处理任务,即使此时有空闲线程存在。

  2. maximumPoolSize:线程池中允许的最大线程数,包括核心线程和非核心线程。当提交的任务数超过corePoolSize时,线程池会尝试创建新的线程来处理任务,但创建的线程数不能超过maximumPoolSize。如果任务队列已满并且已经创建了maximumPoolSize个线程,则线程池会拒绝提交新任务。

  3. keepAliveTime:非核心线程空闲时间的超时时间,当非核心线程在空闲时间超过keepAliveTime时,它们将被终止并从线程池中删除,直到线程池中的线程数小于corePoolSize为止。如果keepAliveTime为0,则非核心线程将不会被终止。

  4. unit:keepAliveTime的时间单位,可以是秒、毫秒、微秒等。

  5. workQueue:任务队列,用于保存等待执行的任务。ThreadPoolExecutor提供了多种类型的队列,如SynchronousQueue、LinkedBlockingQueue、ArrayBlockingQueue等。

  6. threadFactory:线程工厂,用于创建新的线程来执行任务。可以自定义线程工厂来创建自己的线程,例如设置线程名、优先级等。

    Java中,可以使用ThreadFactory接口来自定义线程工厂,该接口只有一个方法newThread(Runnable r),用于创建一个新的线程。常见的线程工厂实现如下:
    
    1. DefaultThreadFactoryJDK提供的默认线程工厂,用于创建普通的线程。它会创建一个新的Thread对象,使用Runnable。并设置线程名、优先级、守护线程标志等属性。
    
    2. NamedThreadFactory:自定义的线程工厂,用于创建有名字的线程。该工厂会为每个线程设置一个名字,格式为prefix-N-thread-M,其中prefix为前缀,N为线程池编号,thread-M为线程编号。
    
    3. PriorityThreadFactory:自定义的线程工厂,用于创建具有优先级的线程。该工厂会为每个线程设置一个优先级,可以在创建线程时指定优先级。
    
    4. CustomizableThreadFactory:自定义的线程工厂,用于创建自定义的线程。该工厂可以自定义线程名、优先级、守护线程标志、线程组等属性。
    
    5. SpringThreadFactorySpring框架提供的线程工厂,用于创建可管理的线程。该工厂会创建一个新的线程,并将其注册到Spring的线程管理器中,以便管理线程的状态和执行情况。
    
    以上是常见的线程工厂实现,当然你也可以自定义实现ThreadFactory接口来创建自己的线程工厂。
    
  7. handler:拒绝策略,当线程池中的线程已经用完并且阻塞队列已满时,ThreadPoolExecutor将拒绝提交新任务。拒绝策略提供了4种处理方式:

    1. CallerRunsPolicy:调用者运行策略,当线程池无法处理新任务时,将任务返回给提交任务的线程来执行(即在提交任务的线程中直接执行任务)。
    
    2. AbortPolicy:线程池默认的拒绝策略。抛弃策略,当线程池无法处理新任务时,直接抛弃该任务并抛出RejectedExecutionException异常。
    
    3. DiscardPolicy:忽视,什么都不会发生。当新任务被提交后直接被丢弃掉,也不会给你任何的通知,相对而言存在一定的风险
    
    4. DiscardOldestPolicy:抛弃最旧的策略,当线程池无法处理新任务时,抛弃队列中最早提交的任务,并尝试重新提交新任务。即:从队列中踢出最先进入队列(最后一个执行)的任务
    
    如果以上4种策略都无法满足需求,可以自定义拒绝策略,只需要实现RejectedExecutionHandler接口即可。
    

运行步骤

当有任务提交到线程池时,线程池会按照以下步骤处理任务

  1. 如果当前活动线程数小于核心线程数,则创建新的工作线程来处理任务,并将任务交给该线程执行,直到当前活动线程数等于核心线程数
  2. 如果当前活动线程数等于核心线程数,则将任务放入阻塞队列中等待执行,直到阻塞队列已满。
  3. 如果阻塞队列已满且当前线程数小于最大线程数,则创建新的工作线程来处理任务,并将任务交给该线程执行,直到达到最大线程数
  4. 如果当前线程数已经达到最大线程数,则拒绝执行新的任务,根据设置的拒绝策略进行处理。
  5. 当线程池中的工作线程完成任务后,它们会继续从阻塞队列中取出任务执行,直到线程池关闭。

参数设置

orePoolSize
核心线程数,默认为1。

设置规则:

  • CPU密集型(CPU密集型也叫计算密集型,指的是运算较多,cpu占用高,读/写I/O(硬盘/内存)较少):corePoolSize = CPU核数 + 1
  • IO密集型(与cpu密集型相反,系统运作,大部分的状况是CPU在等I/O (硬盘/内存) 的读/写操作,此时CPU Loading并不高。):corePoolSize = CPU核数 * 2

keepAliveTime
线程空闲时间,默认为60s,一般设置为默认60s

unit
时间单位,默认为秒

handler
拒绝策略,默认是AbortPolicy,会抛出异常

配置示例

具体使用代码配置参考@Async

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
public class AsyncConfig {

    @Bean(name = "asyncExecutor")
    public ThreadPoolTaskExecutor asyncExecutor() {
        
        //ThreadPoolTaskExecutor是Spring框架提供的一个线程池实现。
        //ThreadPoolTaskExecutor内部维护了ThreadPoolExecutor,并通过各种方法设置其参数(核心线程、队列、最大线程数等)
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        
        // 核心线程数
        executor.setCorePoolSize(10);
        
        // 最大线程数
        executor.setMaxPoolSize(100);
       
        // 队列容量,如果此数字大于0,使用队列LinkedBlockingQueue
        executor.setQueueCapacity(100);
        
        // 线程名称前缀
        executor.setThreadNamePrefix("AsyncThread-");
        
        /**
         * 使用自定义线程工厂
         * 如果不设置则使用默认的DefaultThreadFactory,调用Runnable来执行任务
         */
        executor.setThreadFactory(new TtlThreadFactory());
        
        //初始化
        executor.initialize();
        
        return executor;

    }

}

自定义TtlThreadFactory线程工厂,使用阿里的TtlRunnable执行任务。

import com.alibaba.ttl.TtlRunnable;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;


public class TtlThreadFactory implements ThreadFactory {

    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;

    TtlThreadFactory() {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() :
                Thread.currentThread().getThreadGroup();
        namePrefix = "pool-" +
                poolNumber.getAndIncrement() +
                "-ttlThread-";
    }

    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, TtlRunnable.get(r),
                namePrefix + threadNumber.getAndIncrement(),
                0);
        if (t.isDaemon())
            t.setDaemon(false);
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
}

线程池的任务队列

任务队列(BlockingQueue)指存放被提交但尚未被执行的任务的队列。包括以下几种类型:直接提交的有界的无界的优先任务队列

直接提交的任务队列(SynchronousQueue)

  • 没有容量限制

  • 提交的任务不会被真实的保存在队列中,而总是将新任务提交给线程执行。如果没有空闲的线程,则尝试创建新的线程。如果线程数大于最大线程数,则执行拒绝策略

SynchronousQueue:

在Java线程池中,如果使用SynchronousQueue作为任务队列,它实际上会将任务直接交给线程池中的工作线程进行处理,而不会将任务先存储在队列中。当一个任务提交给线程池后,如果有空闲的线程可用,则任务会立即交给可用的线程进行处理。如果没有可用的线程,新任务的提交将被阻塞,直到有线程可用。

因为SynchronousQueue的容量为0,所以它不会存储任务,可以看作是一个零容量的队列。这也意味着,如果所有线程都在处理任务并且没有空闲的线程可用,新的任务提交将被阻塞,直到有线程空闲为止。

需要注意的是,使用SynchronousQueue作为任务队列可能会导致线程池的负载非常高,因为每个任务都会直接交给工作线程处理。如果任务提交速度过快,而线程池的处理速度跟不上,可能会导致系统负载过高。因此,在使用SynchronousQueue作为任务队列时,需要谨慎评估任务提交的速率和线程池的处理能力。

有界的任务队列(ArrayBlockingQueue、LinkedBlockingQueue)

  • 创建队列时,指定队列的最大容量。
  • 若有新的任务要执行,如果线程池中的线程数小于核心线程数,则会优先创建新的线程。若大于核心线程数,则会将新任务加入到队列中等待。
  • 若队列已满,无法加入。如果总线程数不大于线程最大数,则创建新的线程执行任务。若大于线程最大数,则执行拒绝策略。

队列

  1. ArrayBlockingQueue:一个基于数组结构的有界阻塞队列,按FIFO(先进先出)原则对元素进行排序。
  2. LinkedBlockingQueue:一个基于链表结构的有界阻塞队列,按FIFO原则对元素进行排序。可以指定容量,如果不指定则默认为Integer.MAX_VALUE

无界的任务队列(LinkedBlockingQueue)

  • 与有界队列相比,除非系统资源耗尽,否则不存在任务入队失败的情况。也可以说没有容量限制

  • 若有新的任务要执行,如果线程池中的线程数小于核心线程数,线程池会创建新的线程。若大于核心线程数,此时又没有空闲的线程资源,则任务直接进入等待队列。

  • 当线程池中的线程数达到核心线程数后,线程池不会创建新的线程。

若任务创建和处理的速度差异很大,无界队列将保持快速增长,直到耗尽系统内存。

使用无界队列将会导致核心线程数所有线程都在忙时,新任务全都在队列中等待。这样,创建的线程就不会超过 核心线程数(因此,最大线程数 的值也就无效了)。

适用场景:

当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;或当任务量非常大时,无界任务队列可以避免任务被拒绝的情况

例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

队列

  • LinkedBlockingQueue:除了作为有界队列的选项外,它还可以作为无界队列。如果不指定容量,则默认为Integer.MAX_VALUE

ThreadPoolTaskExecutor的源码中,创建队列时,如果指定容量大于0,使用LinkedBlockingQueue,小于0使用SynchronousQueue

image-20231030105600528

优先任务队列(PriorityBlockingQueue)

  • 带有执行优先级的队列。是一个特殊的无界队列。
  • ArrayBlockingQueueLinkedBlockingQueue都是按照先进先出算法来处理任务。而PriorityBlockingQueue可根据任务自身的优先级顺序先后执行(总是确保高优先级的任务先执行)

优先任务队列是一种根据任务的优先级进行排序的队列,当任务到达线程池时,线程池会根据任务的优先级将任务插入到合适的位置,优先级高的任务会被优先执行。

PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列,元素按照它们的自然顺序或者通过构造函数提供的Comparator进行排序。


java异步使用

Java 实现异步有以下几种方法:

  1. 回调函数(Callback):将异步操作完成后执行的代码封装成回调函数,传递给异步操作,等异步操作完成后调用回调函数进行处理。
  2. Future 和 Promise:通过 Future 和 Promise 来管理异步操作的结果,Future 表示一个异步操作的结果,Promise 用于设置异步操作的结果。
  3. CompletableFuture:Java 8 引入的 CompletableFuture 类,提供了一种更为便捷的异步编程方式,可以轻松地进行异步操作的组合和串联。
  4. Reactive 编程:使用类似 RxJava、Reactor、Akka 等框架,采用响应式编程的方式实现异步操作。

总之,Java 实现异步的方法有很多,选择适合自己项目的方式即可。

回调函数

以下是使用回调函数实现异步的代码示例:

public interface Callback {
    void onSuccess(String result);
    void onFailure(Exception exception);
}


public class AsyncTask {
    public static void doSomethingAsync(Callback callback) {
        new Thread(() -> {
            try {
                // 模拟异步操作
                Thread.sleep(1000);
                String result = "异步操作的结果";
                callback.onSuccess(result);
            } catch (Exception e) {
                callback.onFailure(e);
            }
        }).start();
    }
}


public class Main {
    public static void main(String[] args) {
        System.out.println("开始执行异步操作");
        AsyncTask.doSomethingAsync(new Callback() {
            @Override
            public void onSuccess(String result) {
                System.out.println("异步操作成功,结果为:" + result);
            }
            @Override
            public void onFailure(Exception exception) {
                System.out.println("异步操作失败,异常为:" + exception.getMessage());
            }
        });
        System.out.println("异步操作执行中,请稍等...");
    }
}

以上代码中,定义了一个 Callback 接口,包含 onSuccessonFailure 两个方法。

AsyncTask 类中的 doSomethingAsync 方法模拟了一个异步操作,该方法接收一个 Callback 对象作为参数,异步操作完成后调用 Callback 对象的 onSuccessonFailure 方法进行处理。

Main 类中,调用 AsyncTask.doSomethingAsync 方法,传入一个 Callback 对象,然后输出异步操作执行中的提示信息。当异步操作完成后,根据操作结果调用相应的方法进行处理。


Future和Promise

Future

Future是一个表示异步计算结果的接口。当我们发起一个异步任务时,可以通过Future来获取任务的结果。

Future接口提供了一些方法来检查任务是否已完成、等待任务完成以及获取任务的结果。

例如,可以使用isDone()方法来检查任务是否已完成,使用get()方法来获取任务的结果(如果任务已完成,没有完成的话,调用get方法可能会导致调用线程阻塞)。

Promise

Promise是Future的一个扩展接口,它提供了一些额外的方法来操作Future的结果。

Promise比Future多了以下方法:

  • 通过调用set()方法或setException()方法来设置任务的结果或异常
  • 提供了isCompleted()方法来检查任务是否已完成
  • 调用cancel()方法来取消任务的执行

代码示例

下面是使用Java中的Future和Promise的一个简单示例:

import java.util.concurrent.*;

public class FuturePromiseExample {
    
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        
        //定长线程池,2个
        ExecutorService executor = Executors.newFixedThreadPool(2);
        
        // 使用Future获取异步任务的结果:从线程池拿出一个线程并执行其run方法,返回一个42
        Future<Integer> future = executor.submit(() -> {
            Thread.sleep(1000);
            return 42;
        });
        
        System.out.println("Waiting for the future...");
        
        //如果任务未完成,等1秒
        while (!future.isDone()) {
            Thread.sleep(100);
        }
        
        System.out.println("The future is done. Result: " + future.get());
        
        // 使用Promise设置异步任务的结果
        Promise<Integer> promise = new Promise<>();
        executor.submit(() -> {
            Thread.sleep(1000);
            promise.set(42);
        });
        
        System.out.println("Waiting for the promise...");
        while (!promise.isCompleted()) {
            Thread.sleep(100);
        }
        
        System.out.println("The promise is completed. Result: " + promise.get());
    }
}

class Promise<T> implements Future<T> {
    
    private final Object lock = new Object();
    private T result;
    private Throwable exception;
    private boolean completed = false;
    
    public void set(T result) {
        synchronized (lock) {
            if (!isDone()) {
                this.result = result;
                completed = true;
                lock.notifyAll();
            }
        }
    }
    
    public void setException(Throwable exception) {
        synchronized (lock) {
            if (!isDone()) {
                this.exception = exception;
                completed = true;
                lock.notifyAll();
            }
        }
    }
    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
        throw new UnsupportedOperationException();
    }
    @Override
    public boolean isCancelled() {
        return false;
    }
    @Override
    public boolean isDone() {
        synchronized (lock) {
            return completed;
        }
    }
    @Override
    public T get() throws InterruptedException, ExecutionException {
        synchronized (lock) {
            while (!isDone()) {
                lock.wait();
            }
            if (exception != null) {
                throw new ExecutionException(exception);
            }
            return result;
        }
    }
    @Override
    public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        throw new UnsupportedOperationException();
    }
}

在上面的示例中,我们使用了ExecutorService来执行异步任务。

首先,我们使用Future来获取异步任务的结果。我们通过executor.submit()方法来提交一个任务,然后调用future.get()方法来等待任务完成并获取其结果。

然后,我们使用Promise来设置异步任务的结果。我们定义了一个Promise类,它实现了Future接口,并提供了set()方法来设置任务的结果。我们通过executor.submit()方法来提交一个任务,并在任务完成后调用promise.set()方法来设置任务的结果。我们可以通过调用promise.get()方法来等待任务完成并获取其结果。

需要注意的是,上面的Promise实现只是一个简单的示例,它没有考虑线程安全和超时等问题。在实际应用中,需要根据具体情况来选择合适的FuturePromise实现,并对其进行适当的线程安全和错误处理。


CompletableFuture

在Java 8之前,Future接口的功能比较有限,只能用来获取异步任务的结果。

而在Java 8中引入了CompletableFuture类,它实现了Future和Promise的功能,并提供了更强大的异步编程支持。

CompletableFuture类可以用来构建复杂的异步任务流水线,并提供了一系列的方法来处理任务的结果、异常和取消操作。

public class CompletableFuture<T> implements Future<T>, CompletionStage<T> 
    
CompletableFutureFuture得到了对于异步结果的相关操作,比如get()isDone()CompletableFutureCompletionStage得到了对于异步任务的操作,比如thenApply、thenAccept、thenCompose等,用于定义异步任务完成后的处理逻辑

默认线程池ForkJoinPool

在不指定使用自定义线程池情况下,CompletableFuture中所有(用了线程池的)方法默认使用的线程池为ForkJoinPool

ForkJoinPool是Java中提供的一个特殊的线程池,用于支持分而治之(分治法)的并行计算任务。它是ExecutorService的一个具体实现,是一个基于工作窃取算法的线程池。专门用于执行ForkJoinTask的任务。

ForkJoinPool的特点和用途如下:

  1. 工作窃取(Work Stealing)算法: ForkJoinPool采用工作窃取算法,即线程可以从其他线程的任务队列中窃取任务执行。这种算法可以使得线程在任务处理完成后找到新的任务,提高线程的利用率和整体的并行性能。因此它非常适合用于执行大量的、相对短暂的任务
  2. 分而治之的任务执行模型: ForkJoinPool特别适用于分而治之的任务。通过将大任务划分为更小的子任务进行并行处理,然后将子任务的结果合并,从而实现高效的并行计算。
  3. 工作线程的管理: ForkJoinPool会根据需要创建和回收工作线程,线程的数量可以动态地增长和减少,以适应任务的负载和系统资源的情况。
  4. 异步任务支持: ForkJoinPool不仅支持通过fork()join()方法执行分治任务,还支持通过submit()方法提交异步任务,并通过Future对象获取任务的执行结果。

使用ForkJoinPool的步骤一般包括以下几个步骤:

  1. 创建ForkJoinPool对象,可以通过ForkJoinPool的构造方法指定并行度(线程数量)。
  2. 创建一个继承自RecursiveTaskRecursiveAction的任务类,实现任务的分解和合并逻辑。
  3. 在任务类中重写compute()方法,定义具体的任务逻辑。
  4. 创建任务对象,并通过调用invoke()submit()方法将任务提交给ForkJoinPool执行。
  5. 最后,通过join()get()方法获取任务的执行结果。

需要注意的是,ForkJoinPool适用于处理大规模的可分解任务,并行计算的效果取决于任务的划分和合并逻辑。因此,在使用ForkJoinPool时,需要合理划分任务和设计任务的逻辑,以充分发挥其并行计算的优势。

ForkJoinPool.commonPool()创建共享线程池

ForkJoinPool.commonPool()是一个静态方法,用于获取一个共享的ForkJoinPool实例。

这个共享的ForkJoinPool是一个全局线程池,通常用于执行ForkJoin任务。它这个线程池会自适应地调整线程数量以处理任务,因此它非常适合用于执行大量的、相对短暂的任务

具体来说,commonPool()方法返回一个ForkJoinPool实例,这个实例的线程数由Runtime.getRuntime().availableProcessors()方法决定,它等于当前系统的CPU核心数。这是因为在大多数情况下,线程数量应该与CPU核心数量相匹配,以提高并行处理的效率
commonPool()方法返回的ForkJoinPool实例还具有以下特点:

  1. 它是一个无界的线程池,当需要时可以创建新的线程。
  2. 它使用WorkQueue来存储任务,并使用工作窃取算法来调度线程执行任务。
  3. 它默认使用ForkJoinPool.defaultForkJoinWorkerThreadFactory作为工作线程的创建工厂,该工厂创建的线程都是daemon线程。
  4. 它使用ForkJoinPool.defaultUncaughtExceptionHandler作为默认的未捕获异常处理器,用于处理工作线程中未捕获的异常。
    总之,ForkJoinPool.commonPool()返回的线程池是一个自适应的、基于工作窃取算法的线程池,它能够高效地处理大量的、相对短暂的任务。

ForkJoinPoolThreadPool的不同

ForkJoinPool适合用于以下场景:

  • 任务是可以递归划分的,可以将大任务划分为更小的子任务。
  • 任务之间的计算量差异较大,有些任务可能会比其他任务更耗时。
  • 任务之间的依赖性较低,可以并行执行。
  • 对于CPU密集型任务,ForkJoinPool可以更好地利用多核CPU的计算能力。

ThreadPool适合用于以下场景:

  • 需要处理大量的并发任务,但任务之间没有明显的递归划分。
  • 任务之间的计算量相对均匀,没有明显的计算差异。
  • 任务之间的依赖性较高,需要严格按照提交顺序执行。
  • 对于I/O密集型任务,ThreadPool可以更好地利用线程等待I/O完成的时间。

需要根据具体的场景和需求来选择使用ForkJoinPool还是ThreadPool。ForkJoinPool适用于递归划分的任务和CPU密集型任务,而ThreadPool适用于大量并发任务和I/O密集型任务。如果任务之间的计算量差异大且可以并行执行,可以考虑使用ForkJoinPool,否则可以使用ThreadPool。

下面展示CompletableFuture得具体使用方法

1.创建异步任务

带返回值的异步

supplyAsync

supplyAsync是创建带有返回值的异步任务。它有如下两个方法,一个是使用默认线程池(ForkJoinPool.commonPool())的方法,一个是带有自定义线程池的重载方法

// 带返回值异步请求,默认线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
 
// 带返回值的异步请求,可以自定义线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

测试代码:

public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
            System.out.println("do something....");
            return "result";
        });
 
        //等待任务执行完成
        System.out.println("结果->" + cf.get());
}
 
 
public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 自定义线程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
            System.out.println("do something....");
            return "result";
        }, executorService);
 
        //等待子任务执行完成
        System.out.println("结果->" + cf.get());
}

测试结果

image-20230828092407198


不带返回值的异步

runAsync

runAsync是创建没有返回值的异步任务。它有如下两个方法,一个是使用默认线程池(ForkJoinPool.commonPool())的方法,一个是带有自定义线程池的重载方法

// 不带返回值的异步请求,默认线程池
public static CompletableFuture<Void> runAsync(Runnable runnable)
 
// 不带返回值的异步请求,可以自定义线程池
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)

测试代码:

public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Void> cf = CompletableFuture.runAsync(() -> {
            System.out.println("do something....");
        });
 
        //等待任务执行完成
        System.out.println("结果->" + cf.get());
}
 
 
public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 自定义线程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        CompletableFuture<Void> cf = CompletableFuture.runAsync(() -> {
            System.out.println("do something....");
        }, executorService);
 
        //等待任务执行完成
        System.out.println("结果->" + cf.get());
}

测试结果:

image-20230828092911596

获取任务结果的方法
// 如果完成则返回结果,否则就抛出具体的异常
public T get() throws InterruptedException, ExecutionException 
 
// 最大时间等待返回结果,否则就抛出具体异常
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
 
// 完成时返回结果值,否则抛出unchecked异常。为了更好地符合通用函数形式的使用,如果完成此 CompletableFuture所涉及的计算引发异常,则此方法将引发unchecked异常并将底层异常作为其原因
public T join()
 
// 如果完成则返回结果值(或抛出任何遇到的异常),否则返回给定的 valueIfAbsent。
public T getNow(T valueIfAbsent)
 
// 如果任务没有完成,返回的值设置为给定值
public boolean complete(T value)
 
// 如果任务没有完成,就抛出给定异常
public boolean completeExceptionally(Throwable ex) 

2.异步回调

有入参有返回值的异步回调

thenApply

thenApply 表示某个任务执行完成后执行的动作,即回调方法,会将任务的执行结果(即方法返回值)作为入参传递到回调方法中,带有返回值。

测试代码:

public static void main(String[] args) throws ExecutionException, InterruptedException {
        
    	CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            return 1;
        });
		
    	//cf2调用了cf1
        CompletableFuture<Integer> cf2 = cf1.thenApply((result) -> {
            System.out.println(Thread.currentThread() + " cf2 do something....");
            result += 2;
            return result;
        });
        //等待任务1执行完成
        System.out.println("cf1结果->" + cf1.get());
        //等待任务2执行完成
        System.out.println("cf2结果->" + cf2.get());
}

如上代码,cf1调用了自己的回调函数,并将自己的任务执行结果传入到回调函数中,给cf2使用

  • 只有在cf1的任务执行完毕后,thenApplyAsync方法才会执行。也就是cf2会等待cf1完成后再完成自己的任务,但二者开始执行的先后顺序是不确定的(cf2结果打印永远在cf1打印之后
  • 而且,thenApply方法是同步的,会用调用它的线程来执行,按照上面代码也就是主线程(cf2与主线程是同一个

image-20230828103545292

thenAcceptAsync

thenApply相同,都是异步回调,差异点在于:

  • thenAcceptAsync不会用调用者线程执行,而是用新线程执行
  • thenAcceptAsync可以使用自定义线程池,thenApply不可以

image-20230828104508569

测试代码:

 public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println(Thread.currentThread()+"main thread....");

        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            return 1;
        });

        CompletableFuture<Integer> cf2 = cf1.thenApplyAsync((result) -> {
            System.out.println(Thread.currentThread() + " cf2 do something....");
            result += 2;
            return result;
        });
        //等待任务1执行完成
        System.out.println("cf1结果->" + cf1.get());
        //等待任务2执行完成
        System.out.println("cf2结果->" + cf2.get());
}

结果:

可以看到执行thenAcceptAsync的线程,不是主线程

image-20230828104217882


有入参无返回值的异步回调

thenAccept

将执行结果即方法返回值作为入参传递到回调方法中,无返回值。同步,即与调用者公用一个线程。

测试代码:

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

        System.out.println(Thread.currentThread()+"main thread....");
        
        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            return 1;
        });

        CompletableFuture<Void> cf2 = cf1.thenAccept((result) -> {
            System.out.println(Thread.currentThread() + " cf2 do something....");
        });

        //等待任务1执行完成
        System.out.println("cf1结果->" + cf1.get());
        //等待任务2执行完成
        System.out.println("cf2结果->" + cf2.get());
}

结果:

image-20230828113054825

thenAcceptAsync

thenAccept基本一致。不同在于它是异步,而且可以自定义线程池

image-20230828113216726

测试代码:

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

        System.out.println(Thread.currentThread()+"main thread....");

        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            return 1;
        });

        CompletableFuture<Void> cf2 = cf1.thenAcceptAsync((result) -> {
            System.out.println(Thread.currentThread() + " cf2 do something....");
        });

        //等待任务1执行完成
        System.out.println("cf1结果->" + cf1.get());
        //等待任务2执行完成
        System.out.println("cf2结果->" + cf2.get());
}

结果:

image-20230828113311232


无入参无返回值的异步回调

thenRun

无入参,无返回值。同步,与调用者共用一个线程。

测试代码:

public static void main(String[] args) throws ExecutionException, InterruptedException {
		System.out.println(Thread.currentThread()+"main thread....");
		
        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            return 1;
        });
 
        CompletableFuture<Void> cf2 = cf1.thenRun(() -> {
            System.out.println(Thread.currentThread() + " cf2 do something....");
        });
 
        //等待任务1执行完成
        System.out.println("cf1结果->" + cf1.get());
        //等待任务2执行完成
        System.out.println("cf2结果->" + cf2.get());
}

结果:

image-20230828112221509

thenRunAsync

无入参,无返回值。异步,不与调用者共用一个线程,会开启新的。可使用自定义线程池。

image-20230828112453934

测试代码:

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

        System.out.println(Thread.currentThread()+"main thread....");

        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            return 1;
        });

        CompletableFuture<Void> cf2 = cf1.thenRunAsync(() -> {
            System.out.println(Thread.currentThread() + " cf2 do something....");
        });

        //等待任务1执行完成
        System.out.println("cf1结果->" + cf1.get());
        //等待任务2执行完成
        System.out.println("cf2结果->" + cf2.get());
    }

结果:

image-20230828112413312


有异常无返回值的异步回调

whenComplete

可以将执行结果或者执行期间抛出的异常传递给回调方法,如果发生异常,回调函数内的获取的执行结果为null,而且不能再外部用get方法获取,否则会报错。

需要注意,此回调方法是同步的,也就是用它的调用者线程执行

测试代码:

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

        System.out.println(Thread.currentThread()+" main thread....");

        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            int a = 1/0;
            return 1;
        });

        CompletableFuture<Integer> cf2 = cf1.whenComplete((result, e) -> {
            System.out.println("上个任务结果:" + result);
            System.out.println("上个任务抛出异常:" + e);
            System.out.println(Thread.currentThread() + " cf2 do something....");
        });

//        //等待任务1执行完成
//        System.out.println("cf1结果->" + cf1.get());
//        //等待任务2执行完成
//        System.out.println("cf2结果->" + cf2.get());
}

结果:

可以看到执行回调的是主线程(调用者线程),并输出异常信息且结果为null

image-20230828110401141

如果放开注解进行get,

System.out.println("cf1结果->" + cf1.get());
System.out.println("cf2结果->" + cf2.get());

则会:

两个get都会报错,因为任务都报错了,回调肯定也报错

image-20230828110037678

whenCompleteAsync

whenComplete基本一致,不同点在于whenCompleteAsync会开启新线程执行,并且可使用自定义线程池

image-20230828111501634

测试代码:

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

        System.out.println(Thread.currentThread()+" main thread....");

        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            int a = 1/0;
            return 1;
        });

        CompletableFuture<Integer> cf2 = cf1.whenCompleteAsync((result, e) -> {
            System.out.println("上个任务结果:" + result);
            System.out.println("上个任务抛出异常:" + e);
            System.out.println(Thread.currentThread() + " cf2 do something....");
        });

//        //等待任务1执行完成
 //       System.out.println("cf1结果->" + cf1.get());
//        //等待任务2执行完成
//        System.out.println("cf2结果->" + cf2.get());
}

结果:

image-20230828111108896


有异常有返回值的异步回调

handle

相比whenComplete,也会传递异常,但是有返回值。同步。

测试代码:

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

        System.out.println(Thread.currentThread()+"main thread....");

        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
             int a = 1/0;
            return 1;
        });

        CompletableFuture<Integer> cf2 = cf1.handle((result, e) -> {
            System.out.println(Thread.currentThread() + " cf2 do something....");
            System.out.println("上个任务结果:" + result);
            System.out.println("上个任务抛出异常:" + e);
            return result+2;
        });

//        System.out.println("cf2结果->" + cf1.get());
//        //等待任务2执行完成
//        System.out.println("cf2结果->" + cf2.get());
    }

结果:

image-20230828113941871

handleAsync

相比whenCompleteAsync,也会传递异常,但是有返回值。异步。可自定义线程池。

测试代码:

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

        System.out.println(Thread.currentThread()+"main thread....");

        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
             int a = 1/0;
            return 1;
        });

        CompletableFuture<Integer> cf2 = cf1.handleAsync((result, e) -> {
            System.out.println(Thread.currentThread() + " cf2 do something....");
            System.out.println("上个任务结果:" + result);
            System.out.println("上个任务抛出异常:" + e);
            return result+2;
        });

//        System.out.println("cf2结果->" + cf1.get());
//        //等待任务2执行完成
//        System.out.println("cf2结果->" + cf2.get());
}

结果:

image-20230828114055221


3.多任务组合处理

两个任务组合全部完成

thenCombinethenAcceptBothrunAfterBoth

这三个方法都是将两个CompletableFuture组合起来处理,只有两个任务都正常完成时,才进行下阶段任务。

注意两个任务中只要有一个执行异常,则将该异常信息作为指定任务的执行结果。

thenCombine 有返回值

将两个任务的执行结果作为所提供函数的参数,且该方法有返回值

测试代码:

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

        System.out.println(Thread.currentThread()+" main thread....");

        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            return 1;
        });

        CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf2 do something....");
            return 2;
        });

        CompletableFuture<Integer> cf3 = cf1.thenCombine(cf2, (a, b) -> {
            System.out.println(Thread.currentThread() + " cf3 do something....");
            return a + b;
        });

        System.out.println("cf3结果->" + cf3.get());
 }

结果:

Thread[main,5,main] main thread....
Thread[ForkJoinPool.commonPool-worker-1,5,main] cf1 do something....
Thread[ForkJoinPool.commonPool-worker-2,5,main] cf2 do something....
Thread[main,5,main] cf3 do something....
cf3结果->3

thenAcceptBoth 无返回值

thenAcceptBoth同样将两个任务的执行结果作为方法入参,但是无返回值

测试代码:

public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println(Thread.currentThread()+"main thread....");

        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            return 1;
        });

        CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf2 do something....");
            return 2;
        });

        CompletableFuture<Void> cf3 = cf1.thenAcceptBoth(cf2, (a, b) -> {
            System.out.println(Thread.currentThread() + " cf3 do something....");
            System.out.println("a+b:"+(a + b));
        });

        System.out.println("cf3结果->" + cf3.get());
}

结果:

Thread[main,5,main]main thread....
Thread[ForkJoinPool.commonPool-worker-1,5,main] cf1 do something....
Thread[ForkJoinPool.commonPool-worker-1,5,main] cf2 do something....
Thread[main,5,main] cf3 do something....
a+b:3
cf3结果->null

runAfterBoth 没有入参,也没有返回值

没有入参,也没有返回值

测试代码:

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

        System.out.println(Thread.currentThread()+"main thread....");
        
        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            return 1;
        });

        CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf2 do something....");
            return 2;
        });

        CompletableFuture<Void> cf3 = cf1.runAfterBoth(cf2, () -> {
            System.out.println(Thread.currentThread() + " cf3 do something....");
        });

        System.out.println("cf3结果->" + cf3.get());

}

结果:

Thread[main,5,main]main thread....
Thread[ForkJoinPool.commonPool-worker-1,5,main] cf1 do something....
Thread[ForkJoinPool.commonPool-worker-2,5,main] cf2 do something....
Thread[main,5,main] cf3 do something....
cf3结果->null

两个任务组合任一完成

applyToEitheracceptEitherrunAfterEither。这三个方法都是将两个CompletableFuture组合起来处理,当有一个任务正常完成时,就会进行下阶段任务。

注意两个任务中只要有一个执行异常,则将该异常信息作为指定任务的执行结果。

thenCombine 有返回值

会将两个任务的执行结果作为所提供函数的参数,且该方法有返回值

测试代码:

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

        System.out.println(Thread.currentThread()+" main thread....");

        CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf1 do something....");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "cf1 任务完成";
        });

        CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf2 do something....");
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "cf2 任务完成";
        });

        CompletableFuture<String> cf3 = cf1.applyToEither(cf2, (result) -> {
            System.out.println("接收到" + result);
            System.out.println(Thread.currentThread() + " cf3 do something....");
            return "cf3 任务完成";
        });

        System.out.println("cf3结果->" + cf3.get());
}

结果:

cf1完成后,cf3就完成了,不等cf2

Thread[main,5,main] main thread....
Thread[ForkJoinPool.commonPool-worker-1,5,main] cf1 do something....
Thread[ForkJoinPool.commonPool-worker-2,5,main] cf2 do something....
接收到cf1 任务完成
Thread[ForkJoinPool.commonPool-worker-1,5,main] cf3 do something....
cf3结果->cf3 任务完成

acceptEither 无返回值

acceptEither同样将已经完成任务的执行结果作为方法入参,但是无返回值

测试代码:

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

        System.out.println(Thread.currentThread()+" main thread....");

        CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf1 do something....");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "cf1 任务完成";
        });

        CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf2 do something....");
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "cf2 任务完成";
        });

        CompletableFuture<Void> cf3 = cf1.acceptEither(cf2, (result) -> {
            System.out.println("接收到" + result);
            System.out.println(Thread.currentThread() + " cf3 do something....");
        });

        System.out.println("cf3结果->" + cf3.get());
}

结果:

cf1完成后,cf3就完成了,不等cf2,而且cf3为null,证明没有返回值

Thread[main,5,main] main thread....
Thread[ForkJoinPool.commonPool-worker-1,5,main] cf1 do something....
Thread[ForkJoinPool.commonPool-worker-2,5,main] cf2 do something....
接收到cf1 任务完成
Thread[ForkJoinPool.commonPool-worker-1,5,main] cf3 do something....
cf3结果->null

runAfterEither 没有入参,也没有返回值

测试代码:

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

        System.out.println(Thread.currentThread()+" main thread....");

        CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf1 do something....");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("cf1 任务完成");
            return "cf1 任务完成";
        });

        CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf2 do something....");
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("cf2 任务完成");
            return "cf2 任务完成";
        });

        CompletableFuture<Void> cf3 = cf1.runAfterEither(cf2, () -> {
            System.out.println(Thread.currentThread() + " cf3 do something....");
            System.out.println("cf3 任务完成");
        });

        System.out.println("cf3结果->" + cf3.get());

}

结果:

Thread[main,5,main] main thread....
Thread[ForkJoinPool.commonPool-worker-1,5,main] cf1 do something....
Thread[ForkJoinPool.commonPool-worker-2,5,main] cf2 do something....
cf1 任务完成
Thread[ForkJoinPool.commonPool-worker-1,5,main] cf3 do something....
cf3 任务完成
cf3结果->null

两个任务组合全部完成

allOf

多个CompletableFuture任务组合,都执行完成后才会执行。

只要有一个任务执行异常,返回的CompletableFuture执行get方法就会抛出异常,如果都是正常执行,则get返回null

测试代码:

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

        System.out.println(Thread.currentThread()+" main thread....");

        CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf1 do something....");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("cf1 任务完成");
            return "cf1 任务完成";
        });

        CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf2 do something....");
                //int a = 1/0; //如果放开,在cf1、cf3任务完成后会报错
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("cf2 任务完成");
            return "cf2 任务完成";
        });

        CompletableFuture<String> cf3 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf2 do something....");
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("cf3 任务完成");
            return "cf3 任务完成";
        });

        //组合3个任务
        CompletableFuture<Void> cfAll = CompletableFuture.allOf(cf1, cf2, cf3);
        System.out.println("cfAll结果->" + cfAll.get());
}

结果:

Thread[main,5,main] main thread....
Thread[ForkJoinPool.commonPool-worker-1,5,main] cf1 do something....
Thread[ForkJoinPool.commonPool-worker-2,5,main] cf2 do something....
Thread[ForkJoinPool.commonPool-worker-3,5,main] cf2 do something....
cf1 任务完成
cf3 任务完成
cf2 任务完成
cfAll结果->null

如果放开cf2的int a = 1/0,则结果为

Thread[main,5,main] main thread....
Thread[ForkJoinPool.commonPool-worker-1,5,main] cf1 do something....
Thread[ForkJoinPool.commonPool-worker-2,5,main] cf2 do something....
Thread[ForkJoinPool.commonPool-worker-3,5,main] cf2 do something....
cf1 任务完成
cf3 任务完成
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
	at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
	at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1908)
	at com.ruoyi.Test.main(Test.java:53)
Caused by: java.lang.ArithmeticException: / by zero
	at com.ruoyi.Test.lambda$main$1(Test.java:31)
	at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1604)
	at java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1596)
	at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
	at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1067)
	at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1703)
	at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:172)
两个任务组合任一完成

anyOf

多个CompletableFuture任务组合,只要有一个任务执行完成,anyOf就会执行

只要有一个任务有异常,则返回的CompletableFuture执行get方法时会抛出异常,如果都是正常执行,则get返回执行完成任务的结果

测试代码:

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

        System.out.println(Thread.currentThread()+" main thread....");

        CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf1 do something....");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("cf1 任务完成");
            return "cf1 任务完成";
        });

        CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf2 do something....");
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("cf2 任务完成");
            return "cf2 任务完成";
        });

        CompletableFuture<String> cf3 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf2 do something....");
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("cf3 任务完成");
            return "cf3 任务完成";
        });

        CompletableFuture<Object> cfAll = CompletableFuture.anyOf(cf1, cf2, cf3);
        System.out.println("cfAll结果->" + cfAll.get());
}

结果:

cf1设置睡眠时间最短,返回cf1

Thread[main,5,main] main thread....
Thread[ForkJoinPool.commonPool-worker-1,5,main] cf1 do something....
Thread[ForkJoinPool.commonPool-worker-2,5,main] cf2 do something....
Thread[ForkJoinPool.commonPool-worker-3,5,main] cf2 do something....
cf1 任务完成
cfAll结果->cf1 任务完成

Spring框架应用点

在Spring 5.0版本中,增加了对Java 8中CompletableFuture的支持。具体地,Spring框架在以下几个地方用到了CompletableFuture:

  1. @Async注解:@Async注解在异步方法调用时,返回的值类型可以是CompletableFuture,表示异步方法执行的结果。这样,异步方法的调用方可以通过CompletableFuture来获取异步方法的执行结果。
  2. WebFlux:WebFlux是Spring框架中的响应式编程模型。在WebFlux中,可以通过Mono和Flux来表示异步结果。其中,Mono表示异步单值结果,而Flux表示异步多值结果。Mono和Flux都是基于CompletableFuture实现的,提供了更加方便的异步操作和响应式编程能力。
  3. Spring Data:Spring Data是Spring框架中的数据访问层。在Spring Data中,可以通过CompletableFuture来实现异步的数据访问操作,提高了系统的并发处理能力。

总之,Spring框架中对CompletableFuture的支持,可以帮助开发者更加方便地实现异步操作和响应式编程,提高系统的并发性能和吞吐量。

  • 21
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java异步并发线程池是一种用于管理和执行多线程异步任务的机制。通过使用线程池,可以有效地控制系统资源,并提高并发性能。核心线程数是线程池中一直存在的线程数量,它们准备就绪并等待异步任务的执行。可以使用ExecutorService接口的实现类Executors来创建线程池,例如使用newFixedThreadPool方法创建一个固定大小的线程池,如下所示:ExecutorService service = Executors.newFixedThreadPool(10); \[1\] 关于Java异步并发和线程池的更多信息,可以参考以下资源: - 参考1:https://wenku.baidu.com/view/a9cdf1c09889680203d8ce2f0066f5335a81672a.html - 参考2:https://www.cnblogs.com/weilx/p/16329743.html \[3\] #### 引用[.reference_title] - *1* *2* [Java中的异步线程池](https://blog.csdn.net/weixin_47409774/article/details/123610455)[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^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Java异步并发和线程池](https://blog.csdn.net/qq_36330274/article/details/127229455)[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^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值