一,线程池的七大参数介绍:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
1)corePoolSize:核心线程数[一直存在],线程池创建好后就绪的线程数量,就等待来接收异步任务去执行 2)setMaximumPoolSize,最大线程数 3)setKeepAliveTime,存活时间:当前线程数量大于核心数量,线程空闲之后存活的时间 ,释放空闲的线程 4)TimeUnit,时间单位 5)BlockingQueue<Runnable>,阻塞队列,如果任务很多,就会将目前多的任务放在队列里,只要有空闲的线程, 就会去队列里面取出新的任务继续执行 6)threadFactory:线程的创建工厂 7)setRejectedExecutionHandler:如果队列满了,按照我们指定的拒绝策略,拒绝执行任务,4种拒绝策略
- 第一种拒绝策略是 AbortPolicy,这种拒绝策略在拒绝任务时,会直接抛出异常 RejectedExecutionException (属于RuntimeException),让你感知到任务被拒绝了,于是你便可以根据业务逻辑选择重试或者放弃提交等策略。
- 第二种拒绝策略是 DiscardPolicy,这种拒绝策略正如它的名字所描述的一样,当新任务被提交后直接被丢弃掉,也不会给你任何的通知,相对而言存在一定的风险,因为我们提交的时候根本不知道这个任务会被丢弃,可能造成数据丢失。
- 第三种拒绝策略是 DiscardOldestPolicy,如果线程池没被关闭且没有能力执行,则会丢弃任务队列中的头结点,通常是存活时间最长的任务,这种策略与第二种不同之处在于它丢弃的不是最新提交的,而是队列中存活时间最长的,这样就可以腾出空间给新提交的任务,但同理它也存在一定的数据丢失风险。
- 第四种拒绝策略是 CallerRunsPolicy,相对而言它就比较完善了,当有新任务提交后,如果线程池没被关闭且没有能力执行,则把这个任务交于提交任务的线程执行,也就是谁提交任务,谁就负责执行任务。这样做主要有两点好处。
- 第一点新提交的任务不会被丢弃,这样也就不会造成业务损失。
- 第二点好处是,由于谁提交任务谁就要负责执行任务,这样提交任务的线程就得负责执行任务,而执行任务又是比较耗时的,在这段期间,提交任务的线程被占用,也就不会再提交新的任务,减缓了任务提交的速度,相当于是一个负反馈。在此期间,线程池中的线程也可以充分利用这段时间来执行掉一部分任务,腾出一定的空间,相当于是给了线程池一定的缓冲期。
二:
1、自定义线程参数配置类,可以在配置文件中配置前三个参数
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@ConfigurationProperties(prefix = "custom.thread")
@Component
@Data
public class ThreadPoolConfigProperties {
//核心线程数
private Integer coreSize;
//最大线程数
private Integer maxSize;
//存活时间
private Integer keepAliveTime;
}
2、在线程的配置中自定义一个线程池并加入到容器中:
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Configuration
@EnableConfigurationProperties(ThreadPoolConfigProperties.class)
public class MyThreadConfig {
@Bean
public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties threadPoolConfigProperties){
return new ThreadPoolExecutor(threadPoolConfigProperties.getCoreSize(),
threadPoolConfigProperties.getMaxSize(),
threadPoolConfigProperties.getKeepAliveTime(),
TimeUnit.SECONDS ,
new LinkedBlockingDeque<>(10000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
}
}
3、在异步编排中使用线程池
1、
CompletableFuture.runAsync 无法感知到方法完成,也没返回结果
public static ExecutorService executor = Executors.newFixedThreadPool(10);
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
int i = 1+1;
System.out.println("运行结果:" + i);
}, executor);
2、
CompletableFuture.supplyAsync 可以感知到方法完成,无返回结果
whenComplete 可以处理正常和异常的计算结果,exceptionally 处理异常情况。
whenComplete 和 whenCompleteAsync 的区别:
whenComplete:是执行当前任务的线程执行继续执行 whenComplete 的任务。
whenCompleteAsync:是执行把 whenCompleteAsync 这个任务继续提交给线程池
来进行执行。
方法不以 Async 结尾,意味着 Action 使用相同的线程执行,而 Async 可能会使用其他线程
执行(如果是使用相同的线程池,也可能会被同一个线程选中执行
handle和 complete 一样,可对结果做最后的处理(可处理异常),可改变返回值
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
int i = 1 + 1;
return i;
}, executor).whenComplete((result, exc) -> {
System.out.println("异步任务成功完成,结果为" + result + ";异常为" + exc);
}).exceptionally(throwable -> 0);//感知异常,返回默认值
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
int i = 1 + 1;
return i;
}, executor).handle((result,thr)->{
if(result!=null){
return result*2;
}
if(thr!=null){
return 1;
}
return 0;
});
3、线程的串行化
1)thenrun:不能获取到上一步的执行结果,无返回值 thenRunAsync(() -> { System.out.println("任务2启动"); }, executor); 2)thenAccept:可以获取到上一步的执行结果,无返回值 thenAcceptAsync((res) -> { System.out.println("任务2启动" + res); }, executor); 3) thenApply:可以获取到上一步的执行结果,有返回值 thenApplyAsync((res) -> { System.out.println("任务2启动" + res); return res; }, executor);
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
int i = 1 + 1;
return i;
}, executor).thenApplyAsync((res) -> {
System.out.println("任务2启动" + res);
return res * 3 + "";
}, executor);
4、线程的组合任务
both:两个线程均需要完成,either:只要任意一个完成就可以
runAfterBothAsync,runAfterEitherAsync 不感知结果,无返回值 thenAcceptBothAsync,acceptEitherAsync 感知结果,无返回值 thenCombineAsync,applyToEitherAsync 感知结果,有返回值
新建两个线程
CompletableFuture<Object> future01 = CompletableFuture.supplyAsync(() -> {
int i =1+1;
System.out.println("任务1线程结束");
return i;
}, executor);
CompletableFuture<Object> future02 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务2线程结束");
return "hello";
}, executor);
//任务1与任务2均结束,则执行任务3,任务3获取不到任务1与任务2的执行结果,任务3没有返回值
future01.runAfterBothAsync(future02,()->{
System.out.println("任务3线程"+Thread.currentThread().getId());
},executor);
//任务1与任务2均结束,则执行任务3,任务3可以任务1与任务2的执行结果,任务3没有返回值
future01.thenAcceptBothAsync(future02,(f1,f2)->{
System.out.println("任务3开始...之前的结果"+f1+f2);
},executor);
//任务1与任务2均结束,则执行任务3,任务3可以任务1与任务2的执行结果,任务3有返回值
CompletableFuture<String> future07 = future01.thenCombineAsync(future02, (f1, f2) -> {
System.out.println("任务3开始");
return (f1.toString() + f2);
}, executor);
System.out.println(future07.get());
//任务1与任务2只要其中一个完成,则执行任务3,任务3获取不到任务1与任务2的执行结果,任务3没有返回值
future01.runAfterEitherAsync(future02,()->{
System.out.println("任务3执行");
},executor);
//任务1与任务2只要其中一个完成,则执行任务3,任务3可以获取执行成功线程的执行结果,任务3没有返回值
future01.acceptEitherAsync(future02,(f1)->{
System.out.println("任务3执行,执行结果:"+f1);
},executor);
//任务1与任务2只要其中一个完成,则执行任务3,任务3可以获取执行成功线程的执行结果,任务3有返回值
CompletableFuture<Integer> future06 = future01.applyToEitherAsync(future02, (f1) -> {
System.out.println("任务3执行" + Thread.currentThread().getId());
return (int) f1 + 100;
}, executor);
System.out.println(future06.get());
allof:等待所有任务完成
anyof:只要有一个任务完成
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {
return andTree(cfs, 0, cfs.length - 1);
}
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) {
return orTree(cfs, 0, cfs.length - 1);
}