1总概
1 频繁创建销毁都是资源的浪费,
2创建线程的数量多少合适?少了会怎么样?
3 什么时候用?小任务使用(单个任务时间短)
4 线程池不要混用,什么任务都用一个线程池,不要随便用
5 使用时要打印线程日志
2 参数的含义
当线程池中corePoolSize使用完毕之后,多出来的任务就会进入缓存队列;
当缓存队列也存放满了之后,还有大量的任务,这时将会创建线程池中支持的最大量的线程,然后执行缓存队列里面的任务,而外面的任务进入缓存队列中;当线程池中的线程达到最大时,缓存队列也存满之后,这时该线程池将会执行拒绝策略。
3线程的策略
默认策略:当任务添加到线程池中被拒绝时,它将抛出 RejectedExecutionException 异常。(该策略下,直接丢弃任务,并抛出RejectedExecutionException异常)
new ThreadPoolExecutor.AbortPolicy();
任务将由调用者线程去执行
CallerRunsPolicy
当任务添加到线程池中被拒绝时,默认情况下它将丢弃被拒绝的任务。(即该策略下,直接丢弃任务,什么都不做)
DiscardPolicy
(该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列)
DiscardOldestPolicy
实现RejectedExecutionHandler接口,可自定义处理器
4 api应用
1:submit() 这个方法特别,如果任务出现异常,异常信息只能通过 .get()方法获取到,才能终止程序
//执行无返回值任务 有异常会抛出异常,终止程序 pool.execute(()->{ int a=5/0; System.out.println(Thread.currentThread().getName()); });
//执行有返回值任务 有异常不会抛出异常,不会终止程序,而是在submit.get()才会返回异常信息,并终止程序 Future<Order> submit = pool.submit(() -> { int a=5/0; Order order = new Order(); return order; }); //如果出现异常,只能通过get()方法获取异常,并终止程序。 submit.get();// 阻塞全部任务直到全部执行完所有任务。 这个是简单写法, 其他方式为必须实现接口 Callable // 如果返回值是统一类型没有问题,但是没有统一类型如何优雅获取返回值在操作数据呢? List<Future<Object>> aa = pool.invokeAll(Arrays.asList(() -> { System.out.println("aa"); return "1"; }, () -> { return 1; })); for (Future<Object> objectFuture : aa) { Object o = objectFuture.get(); }
5延时任务
5.1 timer
- 延迟任务都是由一个线程完成,所有程序都是串行执行。会导致整个程序都慢,
- 第一个任务异常也会导致程序终止
ScheduledExecutorService特点:
1 :当线程池只有一个线程的时候,大家会共用这一个线程,第一个任务做完,放回线程池 中,第二个任务取出继续执行,也就是说,线程不够,会有延时。
2:每个任务都是独立的了,异常不会影响其他任务执行。
3:下图只是举例:不要那么创建线程池。
//每隔一段时间执行一次任务
scheduled.scheduleAtFixedRate(()->{
log.info("定时执行任务");
},1,2,TimeUnit.SECONDS);// 初始的延迟 1秒,每隔2秒执行一次
延时执行任务
scheduled.schedule(() -> {
log.info("schedule2");
}, 1, TimeUnit.SECONDS);
forkjoin
forkjoin: 特点就是任务拆分,将一个大的任务拆分成小的任务,在合并
场景应用:个人觉得比较少,比如查询大量数据,又根据某个规则处理,
本人做过一个需求 excel大量数据插入不同的店铺,觉得可以用这个思想,一个店铺一个任务去处理即可。
1 数据量大处理时间长 2 数据可以按照某个规则统一处理
RecursiveAction:用于没有返回结果的任务
RecursiveTask: 用于有返回结果的任务
@Autowired
TUserDao userDao;
@Test
void Thread1() {
ForkJoinPool forkJoinPool = new ForkJoinPool(5);
int total=100;//查询总共100条数据
forkjoinDemo forkjoinDemo = new forkjoinDemo(false,total,userDao);
List<t_user> invoke = forkJoinPool.invoke(forkjoinDemo);
System.out.println(invoke);
}
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.RecursiveTask;
public class forkjoinDemo extends RecursiveTask<List<t_user>> {
TUserDao userDao ;
int total=100;
boolean flag=false;//是否执行
public forkjoinDemo(boolean flag,int total, TUserDao userDao) {
this.flag=flag;
this.total=total;
this.userDao=userDao;
}
@Override
protected List<t_user> compute() {
if(flag){
//执行方法
return userDao.queryUser();
}
//方式一 有指定批次的时候
forkjoinDemo forkjoinOne1 = new forkjoinDemo(true,50, userDao);
forkjoinDemo forkjoinOne2 = new forkjoinDemo(true,100, userDao);
// 子任务执行(fork)
invokeAll(forkjoinOne1,forkjoinOne2);
// 任务汇总
List<t_user> userList1 = forkjoinOne1.join();
List<t_user> userList2 = forkjoinOne2.join();
userList1.addAll(userList2);
return userList1;
//方式二
//将任务分成几个批次
int bath=100/50;
List<t_user> all = new ArrayList<>();
for (int i = 0; i < bath; i++) {
forkjoinDemo forkjoinDemo = new forkjoinDemo(true,50, userDao);
invokeAll(forkjoinDemo);
List<t_user> join = forkjoinDemo.join();
all.addAll(join);
}
return all;
}
}
6个人常用的1.8CompletableFuture
1:他只有异步方法:带返回值和不带返回值, 带线程池和不带线程池
自己创建线程池,不许使用他的不带线程池。
2:所有方法不会抛异常,只有调用 get().才会抛出异常
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
return asyncSupplyStage(asyncPool, supplier);
}
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor) {
return asyncSupplyStage(screenExecutor(executor), supplier);
}
public static CompletableFuture<Void> runAsync(Runnable runnable) {
return asyncRunStage(asyncPool, runnable);
}
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor) {
return asyncRunStage(screenExecutor(executor), runnable);
}
总结api功能:
总结下面的api: 后缀带Async都是线程池出一个线程执行。不带Async的都是由main方法自己去调用的
thenApplyAsync/thenApply (能接收结果,能返回结果)
相同点:1 承接过来上一个任务的结果,并作处理。2 并且处理完可以返回处理结果
不同点: thenApplyAsync 由线程池在出一个线程执行
thenApply由主方法执行
thenAccept和thenAcceptAsync (能接收结果,无返回)
相同点:1 承接过来上一个任务的结果,并作处理。无返回。
不同点: thenAcceptAsync 由线程池在出一个线程执行
thenAccept由主方法执行
thenRun/ thenRunAsync (无接收,无返回)
相同点:类似于回调函数,
不同点: thenRunAsync 由线程池在出一个线程执行
thenRun由主方法执行
whenComplete/whenCompleteAsync (能接收结果和异常信息,无返回)
相同点:1 承接过来上一个任务的结果,并作处理。无返回。
上一个任务无异常,e为null ,有异常 接收结果为null e是上一个任务的异常信息
不同点: thenRunAsync 由线程池在出一个线程执行
thenRun由主方法执行
@Resource(name = "queryThreadPoolExecutor")
private ThreadPoolExecutor queryThreadPoolExecutor;
@Test
void contextLoads() throws ExecutionException, InterruptedException {
//无返回值应用 例如插入日志
/* CompletableFuture.runAsync(() -> {
userDao.insertSelective(new t_user());
}, queryThreadPoolExecutor);*/
//异步执行并且有返回值
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
System.out.println("supplyAsync==" + Thread.currentThread().getName());
//supplyAsync== pool-2-thread-1
// int a =10/0;
//不会抛异常,只有 future1.get();才会抛出异常
return "future1";
}, queryThreadPoolExecutor);
// thenApplyAsync/thenApply 承接过来上一个任务的结果,并作处理,不
// 同之处在于 thenApplyAsync 由线程池在出一个线程执行
// thenApply由主方法执行
// future1 的返回值承接过来(result) 执行是由线程池中出的一个线程
CompletableFuture<String> stringCompletableFuture = future1.thenApplyAsync(result -> {
System.out.println("thenApplyAsync==" + Thread.currentThread().getName() + "结果:" + result);
//thenApplyAsync== pool-2-thread-2 结果:future1
// future1抛异常了 将不会执行这个方法了
return "thenApplyAsync";
}, queryThreadPoolExecutor);
//future1 的返回值承接过来(result) 执行是由main线程执行了
CompletableFuture<String> stringCompletableFuture1 = future1.thenApply((result) -> {
System.out.println("thenApply==" + Thread.currentThread().getName() + "结果:" + result);
//thenApply== main 结果:future1
// future1抛异常了 将不会执行这个方法了
return "thenApply";
});
// thenAccept和thenAcceptAsync 都是承接过来上一个任务的结果,并作处理,但是处理完都是无返回值
// thenAccept由主方法执行,thenAcceptAsync由线程池再出一个线程执行
future1.thenAccept((result) -> {
System.out.println("thenAccept==" + Thread.currentThread().getName() + "结果:" + result);
// thenAccept== main 结果:future1
// future1抛异常了 将不会执行这个方法了
});
future1.thenAcceptAsync((result) -> {
System.out.println("thenAcceptAsync==" + Thread.currentThread().getName() + "结果:" + result);
// thenAcceptAsync==ForkJoinPool.commonPool-worker-9结果:future1 用它的线程池时的打印
// thenAcceptAsync==pool-2-thread-3 结果:future1 我自己的线程池打印
// future1抛异常了 将不会执行这个方法了
}, queryThreadPoolExecutor);
// thenRun和 thenRunAsync 类似于回调函数, 无入参无返回值
// thenRun 由主方法执行, thenRunAsync 由线程池再出一个线程执行
future1.thenRun(() -> {
System.out.println("thenRun==" + Thread.currentThread().getName());
// thenRun==main
});
future1.thenRunAsync(() -> {
System.out.println("thenRunAsync==" + Thread.currentThread().getName());
// thenRunAsync==pool-2-thread-4
}, queryThreadPoolExecutor);
//whenComplete和whenCompleteAsync 都是承接过来上一个任务的结果,和异常 并作处理,但是处理完都是无返回值
// 无异常时 e为null, 有异常时 result 为null 但是不调用get()不会抛出异常
CompletableFuture<String> stringCompletableFuture2 = future1.whenComplete((result, e) -> {
System.out.println("thenRun==" + Thread.currentThread().getName() + " result:" + result + " e:" + e);
// thenRun==main result:future1 e:null
// thenRun==main result:null e:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
});
CompletableFuture<String> stringCompletableFuture3 = future1.whenCompleteAsync((result, e) -> {
System.out.println("thenRun==" + Thread.currentThread().getName() + " result:" + result + " e:" + e);
// thenRun==pool-2-thread-5 result:future1 e:null
// thenRun==pool-2-thread-5 result:null e:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
}, queryThreadPoolExecutor);
// stringCompletableFuture2.get();
// future1.get();
}
写来写去发现这个文章写的很好,挺全面的。
CompletableFuture使用详解(全网看这一篇就行)_代码搬运工阿新的博客-CSDN博客_completablefuture 详解