一文弄懂Java中的线程池

一、线程

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。
在java中可以通过两种方式创建线程:

  • 继承Thread类创建线程。
  • 实现Runnable接口创建线程。Java不支持类的多重继承,但允许你调用多个接口。所以如果你要继承其他类,当然是调用Runnable接口好了。
  • 使用Callable和Future创建线程。(jdk1.5开始提供)

java.lang.Thread 类的实例就是一个线程但是它需要调用java.lang.Runnable接口来执行,由于线程类本身就是调用的Runnable接口所以你可以继承java.lang.Thread 类或者直接调用Runnable接口来重写run()方法实现线程。和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大,call()方法可以有返回值并且可以抛出异常。运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。

// 继承Thread类创建线程
 public class MyThread extends Thread{       
   public void run(){
      System.out.println(" Thread Running " + Thread.currentThread().getName());
   }
 }
// 实现Runnable接口创建线程
public class MyRunnable implements Runnable{         
    public void run(){
       System.out.println(" Create Thread " + Thread.currentThread().getName());
    }
 }

// 实现Callable接口创建线程
class MyCallable implements Callable<String> {
    public String call() throws Exception {
         return Thread.currentThread().getName();
    }
}

//starting Thread in Java
Thread mythread = new MyThread(); //Thread created not started
mythread.setName("T1");
mythread.start(); 

Thread myrunnable = new Thread(new MyRunnable(),"T2"); //Thread created       
myrunnable.start();

MyCallable td = newMyCallable();
FutureTask<String> result = new FutureTask<>(td);
Thread myCallable = new Thread(result);
myCallable.start();

参考:
https://javarevisited.blogspot.com/2011/02/how-to-implement-thread-in-java.html
https://www.cnblogs.com/3s540/p/7172146.html

二、线程池

在这里插入图片描述

  • Executor:一个接口,其定义了一个接收Runnable对象的方法executor,其方法签名为executor(Runnable command)
  • ExecutorService:是一个比Executor使用更广泛的子类接口,其提供了生命周期管理的方法,以及可跟踪一个或多个异步任务执行状况返回Future的方法
  • AbstractExecutorService:ExecutorService执行方法的默认实现
  • ScheduledExecutorService:一个可定时调度任务的接口
  • ScheduledThreadPoolExecutor:ScheduledExecutorService的实现,一个可定时调度任务的线程池
  • ThreadPoolExecutor:表示一个线程池,可以通过调用Executors的静态工厂方法来创建一个拥有特定功能的线程池并返回一个ExecutorService对象

以上成员均在 java.util.concurrent包中, 是 JDK并发包的核心类。其中ThreadpoolExecutor表示一个线程池。 Executors类则扮演着线程池工厂的角色,通过 Executors可以取得一个拥特定功能的线程池。从 UML图中亦可知, ThreadPoolExecutor类实现了 Executor接口, 因此通过这个接口, 任何 Runnable的对象都可以被 ThreadPoolExecutor线程池调度。

1、ThreadPoolExecutor

在这里插入图片描述
ThreadPoolExecutor提供了四个构造函数:

//五个参数的构造函数
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue)
//六个参数的构造函数-1
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory)
//六个参数的构造函数-2
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler)
//七个参数的构造函数
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

详细参数解释(可以对照上图线程图原理图):

  • corePoolSize。该线程池中核心线程数最大值
  • maximumPoolSize。该线程池中线程总数最大值,线程总数 = 核心线程数 + 非核心线程数。
  • keepAliveTime。该线程池中非核心线程闲置超时时长,一个非核心线程,如果不干活(闲置状态)的时长超过这个参数所设定的时长,就会被销毁掉,如果设置allowCoreThreadTimeOut = true,则会作用于核心线程
  • unit。keepAliveTime的单位,TimeUnit是一个枚举类型,其包括:

TimeUnit.DAYS; //天
TimeUnit.HOURS; //小时
TimeUnit.MINUTES; //分钟
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //纳秒

  • workQueue。该线程池中的任务队列:维护着等待执行的Runnable对象。常用类型包含:

1.SynchronousQueue:这个队列接收到任务的时候,会直接提交给线程处理,而不保留它,如果所有线程都在工作怎么办?那就新建一个线程来处理这个任务!所以为了保证不出现<线程数达到了maximumPoolSize而不能新建线程>的错误,使用这个类型队列的时候,maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大
2.LinkedBlockingQueue:这个队列接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过corePoolSize
3.ArrayBlockingQueue:可以限定队列的长度,接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则发生错误
4.DelayQueue:队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务

  • threadFactory。线程工厂,主要用来创建线程。
  • handler:表示当拒绝处理任务时的策略,有以下四种取值:

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

参考:
Java并发编程:线程池的使用:https://www.cnblogs.com/dolphin0520/p/3932921.html
线程池,这一篇或许就够了:https://www.jianshu.com/p/210eab345423
线程池的使用(线程池重点解析):https://www.cnblogs.com/zzuli/p/9386463.html

2、ForkJoinPool

ForkJoinPool运用了Fork/Join原理,使用“分而治之”的思想,将大任务分拆成小任务分配给多个线程执行,最后合并得到最终结果,加快运算。
在这里插入图片描述
ForkJoinPool里有三个重要的角色:
ForkJoinWorkerThread(下文简称worker):包装Thread;
WorkQueue:任务队列,双向;
ForkJoinTask:worker执行的对象,实现了Future。两种类型,一种叫submission,另一种就叫task。

参考:
线程池篇(五):ForkJoinPool - 1:https://www.jianshu.com/p/de025df55363
线程池篇(五):ForkJoinPool - 2:https://www.jianshu.com/p/6a14d0b54b8d
如何使用 ForkJoinPool 以及原理:http://blog.dyngr.com/blog/2016/09/15/java-forkjoinpool-internals/

三、Java通过Executors提供的线程池

Java通过Executors提供五种线程池,分别为:

  • newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  • newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  • newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行。
  • newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
  • newWorkStealingPool:jdk1.8新增,创建持有足够线程的线程池来支持给定的并行级别,并通过使用多个队列,减少竞争,它需要穿一个并行级别的参数,如果不传,则被设定为默认的CPU数量。(实质是使用ForkJoinPool实现)

示例:

ExecutorService service = Executors.newFixedThreadPool(5);
Future<Integer> future = service.submit(()->{
    try{
        Thread.sleep(5000);
    }catch (Exception e){
        e.printStackTrace();
    }
    return 1;
});
System.out.println(future.get().toString());

参考:
http://www.cnblogs.com/webglcn/p/5265901.html

四、Spring中线程池

Spring框架提供了线程池和定时任务执行的抽象接口:TaskExecutor和TaskScheduler来支持异步执行任务和定时执行任务功能。

  • TaskExecutor:

Spring的TaskExecutor接口与java.util.concurrent.Executor接口相同。该接口具有单个方法(execute(Runnable task)),该方法根据线程池的语义和配置接受要执行的任务。

实现类名对应解释
SyncTaskExecutor该实现类不会执行异步调用。 相反,每次调用都在调用的线程中进行(翻译过来也即同步任务执行器)。 它主要用于不需要多线程的情况,例如在简单的测试用例中。
SimpleAsyncTaskExecutor此实现不会重用任何线程。 相反,它为每次调用启动一个新线程。 但是,它确实支持并发限制,该限制会阻止超出限制的任何调用,直到释放插槽为止。(说简单了,就是要使用了直接创建一个线程)
ConcurrentTaskExecutor此实现是java.util.concurrent.Executor实例的适配器。很少需要直接使用ConcurrentTaskExecutor(官网自己都觉得很少使用,不过相对于ThreadPoolTaskExecutor,官网推荐如果ThreadPoolTaskExecutor不够灵活,无法满足需求,则可以使用ConcurrentTaskExecutor)。
ThreadPoolTaskExecutor杀手锏级的任务调度器(最常用),可以说已经足够满足我们的需求了(除非,非常非常特例才使用ConcurrentTaskExecutor)。官网翻译重要片段:公开了bean属性,用于配置java.util.concurrent.ThreadPoolExecutor并将其包装在TaskExecutor中
WorkManagerTaskExecutor此实现使用CommonJ WorkManager作为其后备服务提供程序,并且是在Spring应用程序上下文中在WebLogic或WebSphere上设置基于CommonJ的线程池集成的中心便利类。
DefaultManagedTaskExecutor此实现在JSR-236兼容的运行时环境(例如Java EE 7+应用程序服务器)中使用JNDI获取的ManagedExecutorService,为此目的替换CommonJ WorkManager。(说明了就是依赖环境)
  • TaskScheduler:

用于在将来的某个时间点调度任务。

TaskScheduler接口是定时器的抽象,它的源代码如下。可以看到,该接口包含了一组方法用于指定任务执行的时间。

注:自带的Scheduled,可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多,本文主要介绍。

public interface TaskScheduler {

    //指定一个具体时间点执行定时任务,可以动态的指定时间,开启任务。只执行一次。
    ScheduledFuture schedule(Runnable task, Trigger trigger);

    ScheduledFuture schedule(Runnable task, Date startTime);

    //指定时间开始执行,循环任务,指定一个间隔周期(毫秒计时)注:不管上一个周期是否执行完,到时间下个周期就开始执行

    ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period);

    //立即执行,循环任务,指定一个执行周期(毫秒计时) 注:不管上一个周期是否执行完,到时间下个周期就开始执行
    ScheduledFuture scheduleAtFixedRate(Runnable task, long period);

    //指定时间开始执行,循环任务,指定一个间隔周期(毫秒计时)注:上一个周期执行完,等待delay时间,下个周期开始执行
    ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay);

    //立即执行,循环任务,指定一个间隔周期(毫秒计时)注:上一个周期执行完,等待delay时间,下个周期开始执行
    ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay);
}
实现类名对应解释
ConcurrentTaskScheduler该类实际继承了ConcurrentTaskExecutor对象。只是实现了TaskScheduler接口,增加了相关定时调度任务的方法。
ThreadPoolTaskSchedulerspring对该类设计原则同ThreadPoolTaskExecutor类。是为了定时调度任务不依赖相关的运行容器(例如weblogic、WebSphere等)。其底层委托给ScheduledExecutorService,向外暴露相关的常见bean配置属性。TaskScheduler接口的默认实现类,可以设置执行线程池数(默认一个线程)。

参考:
https://www.jianshu.com/p/ad7bf4472453

1、ThreadPoolTaskExecutor

其实质还是内部封装ThreadPoolExecutor类。

XML配置

    <!-- Spring线程池 -->
    <bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <!-- 核心线程数 -->
        <property name="corePoolSize" value="5" />
        <!-- 线程池维护线程的最大数量 -->
        <property name="maxPoolSize" value="10" />
        <!-- 允许的空闲时间, 默认60秒 -->
        <property name="keepAliveSeconds" value="60" />
        <!-- 任务队列 -->
        <property name="queueCapacity" value="50" />
        <!-- 线程超过空闲时间限制,均会退出直到线程数量为0 -->
        <property name="allowCoreThreadTimeOut" value="true"/>
        <!-- 对拒绝task的处理策略 -->
        <property name="rejectedExecutionHandler">
            <bean class="java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy" />
        </property>
    </bean>

注解配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@EnableAsync
public class ThreadPoolConfig {
    @Bean(name = "taskExecutor")
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(1 << 2);
        // 设置最大线程数
        executor.setMaxPoolSize(1 << 3);
        // 设置队列容量
        executor.setQueueCapacity(1 << 3);
        // 设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(60);
        // 设置默认线程名称
        executor.setThreadNamePrefix("task-");
        // 设置拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        return executor;
    }
}

使用示例:

@Service
public class TestThreadPool {
    @Resource
    private TaskExecutor taskExecutor;
    public void test(){
        taskExecutor.execute(() -> {
            //todo something
        });

        //或

       taskExecutor.execute(new Runnable() {
            public void run() {
                 // todo something
            }
        });
    }
}

2、ThreadPoolTaskScheduler

这种scheduler机制是task的默认机制,而且从名字上也可以看到它是一种委托到ThreadPool的机制,每个 调度任务 都会分配到 线程池 中的一个 线程 去执行。也就是说,任务是 并发执行,互不影响的。可以将它看成一个 轻量级 的 Quartz,而且使用起来比 Quartz 简单许多,但是适用于 单节点的定时任务调度。

ThreadPoolTaskScheduler是TaskScheduler接口的默认实现类,多线程定时任务执行。可以设置执行线程池数(默认一个线程)。在spring-bean.xml中进行配置
使用task:scheduler标签时,spring会实例化一个ThreadPoolTaskScheduler。

XML配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/task
       http://www.springframework.org/schema/task/spring-task-3.1.xsd">
    <task:scheduler id="demoTaskBean" pool-size="2"/>
    <task:scheduled-tasks scheduler="demoTaskBean">
        <task:scheduled ref="demoTask" method="job1" fixed-rate="100" initial-delay="200" cron="" fixed-delay="300"/>
        <task:scheduled ref="demoTask" method="job2" cron="*/5 * * * * ?"/>
    </task:scheduled-tasks>
</beans>
@Lazy
@Service
public class DemoTask {
    public void job1() {
        System.out.println("Task job1: " + System.currentTimeMillis());
    }
    public void job2() {
        System.out.println("Task job2: " + System.currentTimeMillis());
    }
}

XML 的配置,定义一个 scheduler,然后定义具体任务 scheduled-task。
1、task:scheduler 定义一个 ThreadPoolTaskScheduler, 提供唯一参数指定了池大小。

  • pool-size: 任务池大小。池的大小控制着 task:scheduled 的执行,例如以上例子,若 pool-size = 1,则两个 task 只能依次运行,无法并行执行。

2、task:scheduled-tasks :指定所采用的任务池

  • scheduler: 定义的任务池 Bean 的 ID,若不指定,则会被包装为一个单线程 Executor
  • task:scheduled : 指定具体任务,至少有一个,其参数如下表
参数名说明
initial-delay任务初始延迟,单位 milliseconds
fixed-delay任务固定间隔时间,时间自前一次完成后计算,单位 milliseconds
fixed-rate任务固定间隔时间,时间自前一次开始后计算,单位 milliseconds
croncron 表达式
trigger实现 Trigger 接口的 Bean 的引用
ref具体 Task 的方法所在类
method具体 Task 的方法名

注解配置

使用@Scheduler注解先要使用task:annotation-driven启动注解开关。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.1.xsd">
  <mvc:annotation-driven/>
  <task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
  <task:executor id="myExecutor" pool-size="5"/>
  <task:scheduler id="myScheduler" pool-size="10"/>
</beans>
@Service
public class DemoTask {
    @Scheduled(cron = "5 * * * * ?")
    public void job1() {
        System.out.println("Task job1: " + System.currentTimeMillis());
    }
    @Scheduled(initialDelay = 100, fixedDelay = 1000)
    public void job2() {
        System.out.println("Task job2: " + System.currentTimeMillis());
    }
}

五、监听Future任务结果的解决方案

1、Guava中ListenableFuture实现

ListenableFuture是对原有Future的增强,可以用于监听Future任务的执行状况,是执行成功还是执行失败,并提供响应的接口用于对不同结果的处理。

(1)对任务添加回调函数

public class ListenFutureTest {
    public static void main(String[] args) {
        testListenFuture();
    }

    public static void testListenFuture() {
        System.out.println("主任务执行完,开始异步执行副任务1.....");
        ListeningExecutorService pool = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(5));
        ListenableFuture<String> future = pool.submit(new Task());
        Futures.addCallback(future, new FutureCallback<String>() {
            @Override
            public void onSuccess(String result) {
                System.out.println("成功,结果是:" + result);
            }

            @Override
            public void onFailure(Throwable t) {
                System.out.println("出错,业务回滚或补偿");
            }
        });
        System.out.println("副本任务启动,回归主任务线,主业务正常返回2.....");
    }
}
class Task implements Callable<String> {

    @Override
    public String call() throws Exception {
        TimeUnit.SECONDS.sleep(1);
       // int a =1/0;
        return "task done";
    }
}

(2) 等待获得所有任务执行结果

通过Guava中Futures类中allAsList和successfulAsList阻塞获得所有线程的执行结果。

  • allAsList:对多个ListenableFuture的合并,返回一个当所有Future成功时返回多个Future返回值组成的List对象。

注:当其中一个Future发生异常时候,将程序报错。

  • successfulAsList:和allAsList相似,唯一差别是对于失败或取消的Future返回值用null代替。
ListeningExecutorService pool = MoreExecutors.newDirectExecutorService();
ListenableFuture<String> future1 = pool.submit(() -> "Hello");
ListenableFuture<Integer> future2 = pool.submit(() -> 2);
ListenableFuture<List<Object>> future = Futures.allAsList(future1, future2);
List<Object> resList = future.get();
System.out.println(JSON.toJSONString(resList));

ListenableFuture<List<Object>> successFuture = Futures.successfulAsList(future1, future2);
List<Object> successList= successFuture.get();
System.out.println(JSON.toJSONString(successList));

参考:
https://www.kancloud.cn/wizardforcel/guava-tutorial/106940
http://www.cnblogs.com/whitewolf/p/4113860.html
https://codeday.me/bug/20190121/556991.html

2、jdk1.8中CompletableFuture实现

CompletableFuture类实现了CompletionStage和Future接口。

 * @since 1.8
 */
public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {

Future是Java 5添加的类,用来描述一个异步计算的结果;CompletionStage代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段;

(1)操作

1.创建

通过静态工厂方法创建:

方法名描述
runAsync(Runnable runnable)默认使用ForkJoinPool.commonPool()作为它的线程池执行异步代码。
runAsync(Runnable runnable, Executor executor)使用指定的thread pool执行异步代码。
supplyAsync(Supplier<U> supplier)使用ForkJoinPool.commonPool()作为它的线程池执行异步代码,异步操作有返回值
supplyAsync(Supplier<U> supplier, Executor executor)使用指定的thread pool执行异步代码,异步操作有返回值

注:
(1)runAsync 和 supplyAsync 方法的区别是runAsync返回的CompletableFuture是没有返回值的。
(2)这些线程都是Daemon线程,主线程结束Daemon线程不结束,只有JVM关闭时,生命周期终止。
示例:

CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(()->"hello world!");
String ret = completableFuture.get();
System.out.println(ret);
2.变换
public <U> CompletionStage<U> thenApply(Function<? super T,? extends U> fn);
public <U> CompletionStage<U> thenApplyAsync(Function<? super T,? extends U> fn);
public <U> CompletionStage<U> thenApplyAsync(Function<? super T,? extends U> fn,Executor executor);

以Async结尾的方法都是可以异步执行的,如果指定了线程池,会在指定的线程池中执行,如果没有指定,默认会在ForkJoinPool.commonPool()中执行,有入参有返回值

示例:


String result = CompletableFuture.supplyAsync(() -> "hello").thenApply(s -> s + " world").join();
System.out.println(result);

3.消耗

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);

thenAccept是针对结果进行消耗,因为他的入参是Consumer,有入参无返回值

示例:

CompletableFuture.supplyAsync(() -> "hello").thenAccept(s -> System.out.println(s+" world"));
5.上步运行完就执行下步操作
public CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);

thenRun它的入参是一个Runnable的实例,表示当上步执行完,不关心结果就执行下步操作。
示例:

CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "hello";
}).thenRun(() -> System.out.println("hello world"));
6.合并并返回
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);

需要原来的处理返回值,并且other代表的CompletionStage也要返回值之后,利用这两个返回值,进行转换后返回指定类型的值。
示例

String result = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "hello";
}).thenCombine(CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "world";
}), (s1, s2) -> s1 + " " + s2).join();
System.out.println(result);    //输出:hello world
7.合并并消耗
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);

它需要原来的处理返回值,并且other代表的CompletionStage也要返回值之后,利用这两个返回值,进行消耗。

CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "hello";
}).thenAcceptBoth(CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "world";
}), (s1, s2) -> System.out.println(s1 + " " + s2));
8.都运行完执行下步操作
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);

不关心这两个CompletionStage的结果,只关心这两个CompletionStage执行完毕的动作,之后在进行操作(Runnable)。

CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "s1";
}).runAfterBothAsync(CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "s2";
}), () -> System.out.println("hello world"));
9.异常补偿
public CompletionStage<T> exceptionally(Function<Throwable, ? extends T> fn);

该方法在运行时候不一定执行,只有在出现了异常,才exceptionally进行补偿。
示例:

String result = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    if (1 == 1) {
        throw new RuntimeException("测试一下异常情况");
    }
    return "s1";
}).exceptionally(e -> {
    System.out.println(e.getMessage());
    return "hello world";
}).join();
System.out.println(result);
10.最终执行
public CompletionStage<T> whenComplete(BiConsumer<? super T, ? super Throwable> action);
public CompletionStage<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action);
public CompletionStage<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action,Executor executor);

Future的then/catchError/whenComplete执行时序同Java的try/catch/finallycatchError不一定执行,但whenComplete肯定是最后执行。

String result = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    if (1 == 1) {
        throw new RuntimeException("测试一下异常情况");
    }
    return "s1";
}).whenComplete((s, t) -> {
    System.out.println(s);
    System.out.println(t.getMessage());
}).exceptionally(e -> {
    System.out.println(e.getMessage());
    return "hello world";
}).join();
System.out.println(result);
11.运行完成时,对结果的处理。
public <U> CompletionStage<U> handle(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn,Executor executor);

这里的完成时有两种情况,一种是正常执行,返回值。另外一种是遇到异常抛出造成程序的中断。

String result = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    //出现异常
    if (1 == 1) {
        throw new RuntimeException("测试一下异常情况");
    }
    return "s1";
}).handle((s, t) -> {
    if (t != null) {
        return "hello world";
    }
    return s;
}).join();
System.out.println(result);

(2)主动完成计算

public T get()
public T get(long timeout, TimeUnit unit)
public T getNow(T valueIfAbsent)
public T join()

getNow有点特殊,如果结果已经计算完则返回结果或者抛出异常,否则返回给定的valueIfAbsent值。
join返回计算的结果或者抛出一个unchecked异常(CompletionException)

(3)辅助方法allof和anyof

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
  • allOf方法是当所有的CompletableFuture都执行完后执行计算。
  • anyOf方法是当任意一个CompletableFuture执行完后就会执行计算,计算的结果相同。
CompletableFuture<String> completableFuture1=CompletableFuture.supplyAsync(()->{
    //模拟执行耗时任务
    System.out.println("task1 doing...");
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    //返回结果
    return "result1";
});
CompletableFuture<String> completableFuture2=CompletableFuture.supplyAsync(()->{
    //模拟执行耗时任务
    System.out.println("task2 doing...");
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    //返回结果
    return "result2";
});
CompletableFuture<Object> anyResult=CompletableFuture.anyOf(completableFuture1,completableFuture2);
System.out.println("第一个完成的任务结果:"+anyResult.get());
CompletableFuture<Void> allResult=CompletableFuture.allOf(completableFuture1,completableFuture2);
//阻塞等待所有任务执行完成
allResult.join();
System.out.println("所有任务执行完成");

(4)等待获得所有任务执行结果

如果你用过Guava的Future类,你就会知道它的Futures辅助类提供了很多便利方法,用来处理多个Future,而不像Java的CompletableFuture,只提供了allOf、anyOf两个方法。 比如有这样一个需求,将多个CompletableFuture组合成一个CompletableFuture,这个组合后的CompletableFuture的计算结果是个List,它包含前面所有的CompletableFuture的计算结果,guava的Futures.allAsList可以实现这样的功能,但是对于java CompletableFuture,我们需要一些辅助方法:

public static <T> CompletableFuture<List<T>> sequence(List<CompletableFuture<T>> futures) {
    CompletableFuture<Void> allDoneFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
    return allDoneFuture.thenApply(v -> futures.stream().map(CompletableFuture::join).collect(Collectors.<T>toList()));
}

注:只有当每个操作很复杂需要花费相对很长的时间(比如,调用多个其它的系统的接口;比如,商品详情页面这种需要从多个系统中查数据显示的)的时候用CompletableFuture才合适,不然区别真的不大,还不如顺序同步执行。
参考:
https://www.jianshu.com/p/6f3ee90ab7d3
https://zhuanlan.zhihu.com/p/34921166
https://colobu.com/2016/02/29/Java-CompletableFuture/
http://www.cnblogs.com/cjsblog/p/9267163.html
https://www.baeldung.com/java-completablefuture
https://blog.csdn.net/u012129558/article/details/78962759

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32F103是意法半导体(STMicroelectronics)推出的一款32位单片机系列,属于Cortex-M3内核。下面是一份简要的说明,帮助你了解STM32F103。 1. 内核架构:STM32F103采用ARM Cortex-M3内核,具有较高的性能和低功耗特性。它运行在最高72MHz频率下。 2. 存储器:STM32F103具有不同的存储器选项,包括闪存(Flash)和随机存取存储器(SRAM)。闪存用于存储程序代码和常量数据,SRAM用于存储变量和堆栈。 3. 外设:STM32F103拥有丰富的外设,包括通用定时器、串行通信接口(USART、SPI、I2C)、模数转换器(ADC)、通用输入输出引脚(GPIO)等。这些外设可用于实现各种应用,如控制、通信和传感器接口。 4. 开发环境:对于STM32F103的开发,你可以使用ST提供的官方开发工具——STM32CubeIDE,它是基于Eclipse的集成开发环境。此外,你还可以使用其他第三方软件,如Keil MDK或IAR Embedded Workbench。 5. 编程语言:你可以使用C/C++编程语言进行STM32F103的开发。ST提供了丰富的库函数和示例代码,方便开发者快速上手。 6. 资源:为了更好地了解STM32F103,你可以参考ST官方的技术文档、数据手册和应用笔记。此外,CSDN等网站上也有很多关于STM32F103的教程和案例供你学习参考。 需要注意的是,上述信息只是对STM32F103的一个简要介绍,如果你希望深入了解它的特性和开发方法,建议你查阅更多资料并进行实际的开发练习。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值