多线程基本概念
程序,进程,线程的基本概念
程序:是为完成特定任务,用某种语言编写的一组指令的集合,即指一段静态的代码,静态对象。
进程:是程序的一次执行过程,或是正在运行的一个程序,是一个动态的过程,有它自身的产生,存在和消亡的过程。-------生命周期
线程:进程可进一步细化为线程,是一个程序内部的一条执行路径
线程池
背景:经常创建和销毁,使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
思路:提前创建好多个线程,放入线程池之,使用时直接获取,使用完放回池中。可以避免频繁创建销毁,实现重复利用。类似生活中的公共交通工具。
好处:提高响应速度(减少了创建新线程的时间)
降低资源消耗(重复利用线程池中线程,不需要每次都创建)
便于线程管理
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止
。。。。。。
多线程的使用
线程的创建方式
- 创建一个继承于Thread类的子类 (通过ctrl+o(override)输入run查找run方法)
- 重写Thread类的run()方法
- 创建Thread子类的对象
- 通过此对象调用start()方法
主要重点是多线程在springboot中的使用
线程池的创建、配置
@Configuration@EnableAsyncpublicclassAsyncConfiguration{@Bean("doSomethingExecutor")publicExecutordoSomethingExecutor(){ThreadPoolTaskExecutor executor =newThreadPoolTaskExecutor();// 核心线程数:线程池创建时候初始化的线程数
executor.setCorePoolSize(10);// 最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
executor.setMaxPoolSize(20);// 缓冲队列:用来缓冲执行任务的队列
executor.setQueueCapacity(500);// 允许线程的空闲时间60秒:当超过了核心线程之外的线程在空闲时间到达之后会被销毁
executor.setKeepAliveSeconds(60);// 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
executor.setThreadNamePrefix("do-something-");// 缓冲队列满了之后的拒绝策略:由调用线程处理(一般是主线程)
executor.setRejectedExecutionHandler(newThreadPoolExecutor.DiscardPolicy());
executor.initialize();return executor;}}
异步任务的创建
使用的方式非常简单,在需要异步的方法上加@Async注解
@Slf4j@ServicepublicclassAsyncService{// 指定使用beanname为doSomethingExecutor的线程池@Async("doSomethingExecutor")publicStringdoSomething(String message){
log.info("do something, message={}", message);try{Thread.sleep(1000);}catch(InterruptedException e){
log.error("do something error: ", e);}return message;}}
注意事项
@Async("doSomethingExecutor")
异步任务需要指定对应的线程池未配置线程池,使用的是默认的ThreadPoolTaskExecutor,corePoolSize=8,maxPoolSize、queueCapacity=Integer.MAX_VALUE- @Async注解会在以下几个场景失效,也就是说明明使用了@Async注解,但就没有走多线程 (1)异步方法使用static关键词修饰 (2)异步类不是一个Spring容器的bean(一般使用注解@Component和@Service,并且能被Spring扫描到) (3)SpringBoot应用中没有添加@EnableAsync注解 (4)在同一个类中,一个方法调用另外一个有@Async注解的方法,注解不会生效。原因是@Async注解的方法,是在代理类中执行的
注意:异步方法使用注解@Async的返回值只能为void或者Future及其子类,当返回结果为其他类型时,方法还是会异步执行,但是返回值都是null
Spring Boot的异步任务是对原生Java多线程的封装,使得开发人员无需关心底层线程池的具体配置和管理,可以更加专注于业务逻辑。
任务执行结果的获取、异常的捕捉
任务执行结果的获取
通过Future获取异步任务的执行结果
异步任务返回
// 异步执行的方法, 注解内为自定义线程池类名@Override@Async("asyncExecutor")publicFuture<Integer>test(Integer i){
log.info("{}: {}", i,Thread.currentThread().getName());ThreadUtil.sleep(1,TimeUnit.SECONDS);
log.info("@Async执行:{}", i);returnnewAsyncResult(i);}
获取任务的返回值
Future<Integer> future1 = testService.test(1);Future<Integer> future2 = testService.test(2);// 阻塞,直至 future1 的异步线程执行完毕
log.info("future1结果:{}", future1.get());// 阻塞,直至 future2 的异步线程执行完毕
log.info("future1结果:{}", future2.get());
也可以使用Java 8 中引入的的java.util.concurrent.CompletableFuture实现任务结果的获取;
捕捉任务执行的异常
- 方法一:子线程中try… catch… 最简单有效的方法就是:直接在子线程中进行异常捕获处理,如下代码:
publicclassChildThreadimplementsRunnable{publicvoidrun(){doSomething1();try{// 可能发生异常的方法exceptionMethod();}catch(Exception e){// 处理异常System.out.println(String.format("handle exception in child thread. %s", e));}doSomething2();}}
- 方法二:通过Future的get方法捕捉异常使用线程池提交一个能获取到返回信息的方法,也就是ExecutorService.submit(Callable)在submit之后可以获得一个线程执行结果的Future对象,而如果子线程中发生了异常,通过future.get()获取返回值时,可以捕获到ExecutionException异常,从而知道子线程中发生了异常。代码如下:
publicclassMain{publicstaticvoidmain(String[] args){ExecutorService executorService =Executors.newFixedThreadPool(8);Future future = executorService.submit(newChildThread());try{
future.get();}catch(InterruptedException e){System.out.println(String.format("handle exception in child thread. %s", e));}catch(ExecutionException e){System.out.println(String.format("handle exception in child thread. %s", e));}finally{if(executorService !=null){
executorService.shutdown();}}}}
CompletableFuture
是 Java 8 引入的类,扩展了 Future 的功能,提供了更强大和灵活的异步编程支持。它允许更容易地处理异步操作的结果、异常和组合,以及在操作完成时执行回调等。
- 组合和链式操作
Future:Future 没有提供内置的操作符来对多个异步操作进行链式组合。
CompletableFuture:CompletableFuture 支持在操作完成后进行链式操作,使多个异步操作可以依次执行,以及在其中一个或多个操作完成后执行其他操作。
allOf(CompletableFuture<?>... cfs)
返回一个新的CompletableFuture,当所有给定的CompletableFuture都完成后,这个新的Future才完成。
anyOf(CompletableFuture<?>... cfs)
当任何一个给定的CompletableFuture完成后,这个新的Future就完成。
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> loadFromDB1());
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> loadFromDB2());
// 等待所有任务完成
CompletableFuture<Void> allDone = CompletableFuture.allOf(future1, future2);
// 获取所有结果
CompletableFuture<List<String>> combined = allDone.thenApply(v -> Arrays.asList(future1.join(), future2.join()));
- 异常处理
Future:Future 的异常处理相对有限,通常需要使用 try-catch 块来捕获操作过程中的异常。
CompletableFuture:CompletableFuture 具有更强大的异常处理机制,可以使用 exceptionally()、handle() 等方法来处理操作过程中的异常。
exceptionally(Function<Throwable,? extends T> fn)
当CompletableFuture遇到异常时,使用给定的函数处理异常并返回一个替代结果。
CompletableFuture<String> future =...;CompletableFuture<String> fallback = future.exceptionally(ex ->"Fallback Value");
handle(BiFunction<? super T, Throwable, ? extends U> fn)
不管是正常完成还是异常完成,都会调用给定的函数来处理结果或异常。
CompletableFuture<String> future =...;CompletableFuture<Object> handled = future.handle((result, ex)->{if(ex !=null){returnhandleException(ex);}else{returnprocessSuccess(result);}});
- 回调执行
Future:Future 不支持在操作完成时执行回调操作。
CompletableFuture:CompletableFuture 支持使用 thenApply()、thenCompose()、thenCombine() 等方法来在操作完成后执行回调。
thenApply(Function<? super T,? extends U> fn)
在前一个任务完成之后,根据前一个任务的结果执行一个函数并返回新的CompletableFuture
。thenCompose(Function<? super T,? extends CompletionStage<U>> fn)
类似于thenApply
,但是返回的是一个新的CompletableFuture
,适用于任务之间有依赖的情况。
CompletableFuture<Integer> futureInt =CompletableFuture.supplyAsync(()->10);CompletableFuture<String> futureStr = futureInt.thenApply(result ->"Result is: "+ result);
参考链接
SpringBoot使用多线程
CompletableFuture 和 Future 的区别
CompletableFuture 常用方法
版权归原作者 zhouzipeng_cqu 所有, 如有侵权,请联系我们删除。