在业务开发中,有很多异步场景,为了节约时间或或者提高系统的吞吐量,要做一些异步任务,在Java中要实现异步通常都是Thread,开启一个线程Thread,开启线程有四种方式。
1、初始化线程池的4中方式
1)、继承Thread
2)、实现Runnable接口
3)、实现Callable接口+FutureTask(可以拿到返回结果,可以处理异常)
4)、线程池
方式1和方式2:主进程无法获取线程的运算结果。不适合我们当前的场景。
方式3:主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。
方式4:通过如下两种方式初始化线程池:
Excutors.newFiexedThreadPool(10);
// 或者
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime,TimeUnit unit, workQuene,threadFactory,handler);
通过线程池性能稳定,也可以获取执行结果,并捕获异常。但是,在业务复杂情况下,一个异步调用可能会依赖于另一个异步调用的执行结果。
2、开启线程测试
package com.kun.search.ThredTest;
import java.util.concurrent.*;
/**
* @author zhoukun 86547462@qq.com
* @version 1.0
* @date 2020/12/18 15:40
*
*/
public class ThreadTest {
/**
* 1)、继承Thread
* Thread01 thread = new Thread01();
* thread.start(); // 启动线程
*
* 2)、实现Runnable接口
* Thread02 runable01 = new Thread02()
* new Thread(runable01).start();
*
* 3)、实现Callable接口+FutureTask(可以拿到返回结果,可以处理异常)
* FutureTask<Integer> FutureTask = new FutureTask<>(new Thread03());
* new Thread(futureTask).start();
* // 阻塞等待整个线程执行完成,获取返回结果
* Integer integer = futureTask.get();
*
* 4)、线程池[ExecutorService]
* 给线程池直接提交任务。
* service.execute(new Thread01());
* 创建:
* ①、Excutors
* ②、new ThreadPoolExecutor
*
* Future:可以获取到异步结果
* 方式1和方式2:主进程无法获取线程的运算结果。不适合我们当前的场景。
* 方式3:主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。
* 方式4:通过如下两种方式初始化线程池:
*/
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main...start...");
//我们在以后的代码中,三种启动线程的方式都不用,统一使用线程池启动
ExecutorService executorService = Executors.newFixedThreadPool(10);
FutureTask<Integer> futureTask = new FutureTask<>(new Thred03());
executorService.execute(futureTask);
// 阻塞等待整个线程执行完成,获取返回结果
Integer integer = futureTask.get();
System.out.println(integer);
System.out.println("main...end...");
}
public static class Thread01 extends Thread{
@Override
public void run(){
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("运行结果:" + i);
}
}
public static class Thred02 implements Runnable
{
@Override
public void run() {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("运行结果:" + i);
}
}
public static class Thred03 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("运行结果:" + i);
return i;
}
}
}
二、线程池
开发中为什么使用线程池?
降低资源的消耗
通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗
提高响应速度
因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行。
提高线程的可管理性
线程池会根据当前系统特点对池内的线程进行优化处理,减少线程创建和销毁带来的系统开销。
创建线程池:
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime,TimeUnit unit, workQuene,threadFactory,handler);
源码
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters and default rejected execution handler.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
创建线程池实例
private static void threadPool(){
//原生创建
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
5,
200,
10,
TimeUnit.SECONDS,
new LinkedBlockingDeque<Runnable>(10000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
//Executors创建
Executors.newScheduledThreadPool(); // 定时任务的线程池
Executors.newCachedThreadPool();//core是0,所有都可回收
Executors.newFixedThreadPool();//固定大小,core=max,所有都不可回收
Executors.newSingleThreadExecutor();//单线程的线程池,后台从队列获取任务,挨个执行
}
线程池七大参数:
corePoolSize:[5] 核心线程数[一直存在除非(allowCoreThreadTimeOut)];线程池创建好以后就准备就绪。5个 Thread thread = new Thread(); thread.start();
maximumPoolSize:[200] 最大线程数量;控制资源。
keepAliveTime:存活时间。如果当前的线程数量大于 core数量。释放空闲的线程(maximumPoolSize - corePoolSize)。只要线程空闲的时间大于指定的keepAliveTime。
unit:时间单位
BlockingQueue workQueue:阻塞队列。如果任务有很多,就会将目前多的任务放到队列里面。只要有线程空闲,就会去队列里面取出新的任务继续执行。
threadFactory:线程的创建工厂。
RejectedExecutionHandler handler:如果队列满了,按照我们指定的拒绝策略拒绝执行任务。
工作顺序:
1.线程池创建,准备好core数量的核心线程,准备接受任务。
1.1、core满了,就将再进来的任务放入阻塞队列中。空闲的core就会自己去阻塞队列获取任务。
2、阻塞队列满了,就直接开新线程执行,最大只能开到max指定的数量
3、max满了就用 RejectedExecutionHandler handler 拒绝任务
max都执行完成,有很多空闲,在指定的时间 keepAliveTime 以后,释放 max - core 这些线程。
new LinkedBlockingDeque<>():默认是 Integer 的最大值。会导致内存占满,需要根据自己的业务定。
面试:
一个线程池 core 7, max 20, queue:50, 100并发进来怎么分配?
答:先有 7 个能直接得到执行,接下来 50 个进入阻塞队列,再多开13个线程继续执行。现在 70 个被安排上了。剩下 30 个默认拒绝策略。
注意:当 core满了 不会立即创建新的线程,而是将进来的任务放入到阻塞队列中,当阻塞队列满了之后,才会直接新开线程执行,最大只能开到 Max指定的数量。
三、CompletableFuture异步编排
业务场景:
查询商品详情页的逻辑比较复杂,有些数据还需要远程调用,必然需要花费更多的时间。
假如商品详情页的每个查询,用户需要6.5s 后才能看到商品详情页的内容,这是显然不能接受的。
如果多个线程同时完成这个 6 步操作,也许只需要 1.5s 即可响应完成。
1、创建异步对象
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);
说明:没有指定Executor的方法会使用 ForkJoinPool.commonPool() 作为它的线程池执行异步代码。如果指定线程池,则使用指定的线程池运行。
①、runXxxx都是没有返回结果的,supplyXxx都是可以获取返回结果的
②、可以传入自定义的线程池,否则就用默认的线程池。
实例
public class CompletableTest {
public static ExecutorService executor=Executors.newFixedThreadPool(5);
public static void main(String[] args) {
/** 第一种 runAsync 不支持返回值的 */
CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> {
Integer a=10/0;
System.out.println("runAsync 不支持返回值");
},executor).whenComplete((t,u)->{
System.out.println("runAsync 完成");
System.out.println("异常是:"+u);
});
/** 第二种 supplyAsync 支持返回值 */
CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
int i = 10/0;
return i;
},executor).whenComplete((t, u) -> {
System.out.println("supplyAsync 完成,返回值是" + t);
System.out.println("异常是:"+u);
}).exceptionally(thorwable->{
//出现异常返回默认值
return 10086;
})handle((t,u)->{
//方法执行完成后的处理
if(t!=null)
{
return t*2;
}
if(u!=null)
{
return 0;
}
return 0;
});;
Integer i=supplyAsync.get();
System.out.println(i);
}
}
运行结果:
runAsync 不支持返回值
runAsync 完成
异常是:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
supplyAsync 完成,返回值是null
异常是:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
20172
2、计算完成时回调方法
当CompletableFuture的计算结果完成,或者抛出异常的时候,可以执行特定的Action。主要是下面的方法:
public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action);
方法完成后的感知处理
返回相同的结果或例外,这一阶段的新completionstage,这个阶段完成时,执行特定动作的结果(或 null如果没有)和异常(或 null如果没有)这个阶段。
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action);
返回相同的结果或例外,这一阶段的新completionstage,这个阶段完成时,执行特定动作执行给定的操作这一阶段的默认的异步执行设施,其结果(或 null如果没有)和异常(或 null如果没有)这个阶段作为参数。
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor);
返回相同的结果或例外,这一阶段的新completionstage,这个阶段完成时,执行使用所提供的遗嘱执行人,给出的行动与结果(或 null如果没有)和异常(或 null如果没有)这个阶段作为参数。
public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn);
方法出现异常后的处理,出现异常返回默认值
返回一个新的completablefuture已经完成与给定值。
handle((t,u)
//方法执行完成后的最后处理
3、线程串行化方法
thenApply 方法:当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前任务的返回值。
thenAccept方法:消费处理结果。接收任务的处理结果,并消费处理,无返回结果。
thenRun方法:只要上面的任务执行完成,就开始执行thenRun,只是处理完任务后,执行 thenRun的后续操作
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 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 CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);
只要上面的任务执行完成,就开始执行thenRun,**只是处理完任务后,执行 thenRun的后续操作
每一个方法都对应了三种操作。带有Async默认是异步执行的。这里所谓的异步指的是不在当前线程内执行。带有参数Executor executor的则用指定的线程池方案,不指定的话则用默认的ForkJoinPool.commonPool()
。
说明:(Function<? super T,? extends U> fn)
T : 上一个任务返回结果的类型
U:当前任务的返回值的类型
测试:thenApply()方法
:当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前任务的返回值。
/**
* 线程串行化方法 */
CompletableFuture<String> future3 =CompletableFuture.supplyAsync(() -> {
System.out.println("线程1启动了:"+Thread.currentThread().getId());
return 10;
},executor).whenComplete((t,u)->{
System.out.println("runAsync 完成");
System.out.println("异常是:"+u);
}).thenApplyAsync(res->{
System.out.println("线程2启动了:"+Thread.currentThread().getId());
return "最后结果是:"+res;
},executor);
System.out.println(future3.get());
4、两任务组合 - 都要完成
两个任务必须都完成,触发该任务。
thenCombine
:组合两个future,获取两个future任务的返回结果,并返回当前任务的返回值thenAcceptBoth
:可以感知前两个结果,组合两个future,获取两个future任务的返回结果,然后处理任务,没有返回值。runAfterBoth
:无法感知前两个结果,组合两个future,不需要获取future的结果,只需两个future处理完任务后,处理该任务
public <U,V> CompletableFuture<V> thenCombine(
CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletableFuture<V> thenCombineAsync(
CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletableFuture<V> thenCombineAsync(
CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn, Executor executor);
组合两个future,获取两个future任务的返回结果,并返回当前任务的返回值
public <U> CompletableFuture<Void> thenAcceptBoth(
CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action);
public <U> CompletableFuture<Void> thenAcceptBothAsync(
CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action);
public <U> CompletableFuture<Void> thenAcceptBothAsync(
CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action, Executor executor);
组合两个future,获取两个future任务的返回结果,然后处理任务,没有返回值。
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,
Runnable action);
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,
Runnable action);
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,
Runnable action,
Executor executor);
组合两个future,不需要获取future的结果,只需两个future处理完任务后,处理该任务
测试
/*
两个都要完成
*/
CompletableFuture<Integer> f1 =CompletableFuture.supplyAsync(()->{
System.out.println("线程1启动了:"+Thread.currentThread().getId());
return 10;
},executor);
CompletableFuture<String> f2 =CompletableFuture.supplyAsync(()->{
System.out.println("线程2启动了:"+Thread.currentThread().getId());
return "哈哈";
},executor);
//不能接受前面值,没返回值
// f1.runAfterBothAsync(f2,()->{
// System.out.println("线程3启动了:"+Thread.currentThread().getId());
// },executor);
// //可以接受前面的结果但是无返回值
// f1.thenAcceptBothAsync(f2,(res1,res2)->{
// System.out.println("线程3启动了:"+Thread.currentThread().getId());
// },executor);
//组合两个future,获取两个future任务的返回结果,并返回当前任务的返回值
CompletableFuture<String> future12 =f1.thenCombineAsync(f2,(res1,res2)->{
System.out.println("线程3启动了:"+Thread.currentThread().getId());
return "结果:"+res1+res2;
},executor);
4、两任务组合 - 一个完成
当两个任务中,任意一个future任务完成的时候,执行任务。applyTOEither
:两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值。acceptEither
:两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值。runAfterEither
:两个任务有一个执行完成,不需要获取 future的结果,处理任务,也没有返回值。
测试
/*
只要一个完成
*/
CompletableFuture<Object> fe1 =CompletableFuture.supplyAsync(()->{
System.out.println("二对一线程1启动了:"+Thread.currentThread().getId());
return "哈哈";
},executor);
CompletableFuture<Object> fe2 =CompletableFuture.supplyAsync(()->{
System.out.println("二对一线程2启动了:"+Thread.currentThread().getId());
return 10;
},executor);
System.out.println(fe1.applyToEitherAsync(fe2,res->res.toString(),executor).get());
结果
二对一线程1启动了:14
二对一线程2启动了:14
哈哈
5、多任务组合
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs);
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs);
说明:allOf
:等待所有任务完成anyOf
:只要有一个任务完成
测试
/*
多任务组合
*/
CompletableFuture<Object> futureImg =CompletableFuture.supplyAsync(()->{
System.out.println("商品的图片");
return "10000.jpg";
},executor);
CompletableFuture<Object> futureAttr =CompletableFuture.supplyAsync(()->{
try {
Thread.sleep(3000);
System.out.println("商品的属性");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "透明版+528g";
},executor);
CompletableFuture<Object> futureDesc =CompletableFuture.supplyAsync(()->{
System.out.println("商品的介绍");
return "小米10至尊版";
},executor);
// //等待所有任务完成
// CompletableFuture.allOf(futureAttr,futureDesc,futureImg);
// System.out.println(""+futureImg.get()+futureDesc.get()+futureAttr.get());
//只要有一个任务完成
CompletableFuture com=CompletableFuture.anyOf(futureAttr,futureDesc,futureImg);
System.out.println(com.get());
整体代码测试:
package com.kun.search.ThredTest;
import java.util.concurrent.*;
/**
* @author zhoukun 86547462@qq.com
* @version 1.0
* @date 2020/12/18 18:23
*/
public class CompletableTest {
public static ExecutorService executor=Executors.newFixedThreadPool(5);
public static void main(String[] args) throws ExecutionException, InterruptedException {
/** 第一种 runAsync 不支持返回值的 */
CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> {
System.out.println("runAsync 不支持返回值");
Integer a=10/0;
},executor).whenComplete((t,u)->{
System.out.println("runAsync 完成");
System.out.println("异常是:"+u);
});
/** 第二种 supplyAsync 支持返回值 */
CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
int i = 10/0;
return i;
},executor).whenComplete((t, u) -> {
//方法完成后的感知处理
System.out.println("supplyAsync 完成,返回值是" + t);
System.out.println("异常是:"+u);
}).exceptionally(thorwable->{
//方法出现异常后的处理,出现异常返回默认值
return 10086;
}).handle((t,u)->{
//方法执行完成后的最后处理
if(t!=null)
{
return t*2;
}
if(u!=null)
{
return 0;
}
return 0;
});
Integer i=supplyAsync.get();
System.out.println(i);
/**
* 线程串行化方法 */
CompletableFuture<String> future3 =CompletableFuture.supplyAsync(() -> {
System.out.println("线程1启动了:"+Thread.currentThread().getId());
return 10;
},executor).whenComplete((t,u)->{
System.out.println("runAsync 完成");
System.out.println("异常是:"+u);
}).thenApplyAsync(res->{
System.out.println("线程2启动了:"+Thread.currentThread().getId());
return "最后结果是:"+res;
},executor);
System.out.println(future3.get());
/*
两个都要完成
*/
CompletableFuture<Integer> f1 =CompletableFuture.supplyAsync(()->{
System.out.println("线程1启动了:"+Thread.currentThread().getId());
return 10;
},executor);
CompletableFuture<String> f2 =CompletableFuture.supplyAsync(()->{
System.out.println("线程2启动了:"+Thread.currentThread().getId());
return "哈哈";
},executor);
//不能接受前面值,没返回值
// f1.runAfterBothAsync(f2,()->{
// System.out.println("线程3启动了:"+Thread.currentThread().getId());
// },executor);
// //可以接受前面的结果但是无返回值
// f1.thenAcceptBothAsync(f2,(res1,res2)->{
// System.out.println("线程3启动了:"+Thread.currentThread().getId());
// },executor);
//组合两个future,获取两个future任务的返回结果,并返回当前任务的返回值
CompletableFuture<String> future12 =f1.thenCombineAsync(f2,(res1,res2)->{
System.out.println("线程3启动了:"+Thread.currentThread().getId());
return "结果:"+res1+res2;
},executor);
System.out.println(future12.get());
/*
只要一个完成
*/
CompletableFuture<Object> fe1 =CompletableFuture.supplyAsync(()->{
System.out.println("二对一线程1启动了:"+Thread.currentThread().getId());
return "哈哈";
},executor);
CompletableFuture<Object> fe2 =CompletableFuture.supplyAsync(()->{
System.out.println("二对一线程2启动了:"+Thread.currentThread().getId());
return 10;
},executor);
System.out.println(fe1.applyToEitherAsync(fe2,res->res.toString(),executor).get());
/*
多任务组合
*/
CompletableFuture<Object> futureImg =CompletableFuture.supplyAsync(()->{
System.out.println("商品的图片");
return "10000.jpg";
},executor);
CompletableFuture<Object> futureAttr =CompletableFuture.supplyAsync(()->{
try {
Thread.sleep(3000);
System.out.println("商品的属性");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "透明版+528g";
},executor);
CompletableFuture<Object> futureDesc =CompletableFuture.supplyAsync(()->{
System.out.println("商品的介绍");
return "小米10至尊版";
},executor);
// //等待所有任务完成
// CompletableFuture.allOf(futureAttr,futureDesc,futureImg);
// System.out.println(""+futureImg.get()+futureDesc.get()+futureAttr.get());
//只要有一个任务完成
CompletableFuture com=CompletableFuture.anyOf(futureAttr,futureDesc,futureImg);
System.out.println(com.get());
}
}