之前写了关于java 基本使用多线程的一篇博客,由于java开发最经常使用的是Spring框架使用写这一篇博客进行总结
一、线程池
1、基本概念
ava开辟了一个管理线程的概念,这个概念叫做线程池,线程池的好处就是方便的管理线程,从而减少内存的消耗
2、参数解析
创建线程池可以使用它的子类 ThreadPoolExecutor
其参数为 corePoolsize
maxmumPoolSize
keepAliveTime
workQueue
threadFactory handler
线程池中的corePoolSize就是线程池中的核心线程数量,当线程池当前的个数大于核心线程池的时 候,线程池会回收多出来的线程
maximumPoolSize 就是线程池中可以容纳的最大的线程数量
keepAliveTime 就是线程池中除了核心线程之外的其他的最长可以保留的时间, 因为在线程池核心线程即使在无任务的情况下也不能被清除,非核心线程是有存活的事件,也就是非核心线程保留的最长的空闲时间,而until 就是计算这个时间的单位
workQueue 就是一个等待队列,任务可以存储在任务队列中等待被执行,执行原则(先进先出)
threadFactory 就是创建线程的线程工厂
后一个handler 就是一种拒绝策略
3、使用步骤
1、当线程池小于corePoolSize 的时候,新提交的任务将创建一个新线程,即使此时线程池中存在空闲的线程
2、当线程池达到corePoolSize的时候,新提交的任务将被放入workQueue 中,等待线程池中任务调用执行。
3、当workQueue 已满的时候,而且maximumPoolSize > corePoolSize 的时候 , 新提交的任务会创建新的线程执行任务。
4、当提交的任务数超过maximunPoolSize 时,新提交的任务由 RejectedExecutionHandler 处理
5、当线程池超过了corePoolSize 线程,空闲时间达到KeepAliveTime 的时候,关闭空闲线程
6、当设置allowCoreThreadTimeOut(true) 时,线程池中corePoolSize 线程空闲时间达到keepAliveTime 也将关闭
线程池的拒绝策略:
1、一种就是AbortPolicy 不执行新的任务, 直接抛出异常,提示线程池已满。---直接滚蛋
2、discarPolicy 不执行新的任务也不抛出异常 ----不理睬你
3、 disCardOldSetPolicy 将消息队列中得到的第一个任务替换为当前新进来的任务执行
4、CallRunsPolicy 执行调用execute 来执行当前的任务
4、线程池的类型
CacheThreadPool
可缓存的线程池,该线程池中没有核心的线程,非核心线程的数量为interger.max_vvalue,当需要的时候创建线程来执行任务,没有需要的时候就会回收线程,使用于耗时少 任务量大的时候
SecudleThreadPool
周期性的执行任务的线程池,按照某种特定的计划执行线程中的任务,有核心线程,但也有非核心线程,非核心线程也为无限大,适用于执行周期性任务
SingleThreadPool
只有一条线程来执行任务,适用于有顺序的任务的应用场景
FixedThreadPool
定长的线程池,有核心线程,有核心线程,核心线程即为最大的线程数量,没有非核心线程
5、四种线程池如何使用
使用线程池的返回值ExecutorService ,ExecutorService 是java 提供用于管理线程池的类,该类的两个作用是: 控制线程数量和重用线程
1、 使用CacheThreadPool
Executor.newCacheThreadPool() 可以创建缓存线程池: 首先查看线程池中有没有以前建立的线程,如果有就直接使用,如果没有就创建一个新的线程加入池中(非核心线程数为无限大)
package com.example.demothread.util;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author lenovo
* @version 1.0
* @Date 2022/5/1 15:13
* @Description
*/
@Slf4j
public class MainDemo {
public static void main(String args[]) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(index * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(index);
}
});
}
}
}
线程池为无限大,当执行第二个任务的时候,第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。
2、 newFiedThreadPool
创建一个定长的线程池,可控制现成的最大并发数,超过的线程会在队列中等待
package com.example.demothread.util;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author lenovo
* @version 1.0
* @Date 2022/5/1 15:13
* @Description
*/
@Slf4j
public class MainDemo {
public static void main(String args[]) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
for(int i=0;i<10;i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
try {
log.info("当前线程---{}",Thread.currentThread().getName());
Thread.sleep(1000);
} catch (Exception ex) {
log.error("捕获到异常---{}",ex.getMessage(),ex);
}
}
});
}
}
}
运行结果:
从结果可以看出 核心线程数有3个,一直被使用
3、newScheduledThreadPool
package com.example.demothread.util;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @author lenovo
* @version 1.0
* @Date 2022/5/1 15:13
* @Description
*/
@Slf4j
public class MainDemo {
public static void main(String args[]) {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
log.info("延迟1秒后每3秒执行一次");
log.info("当前线程---{}",Thread.currentThread().getName());
}
},1,3, TimeUnit.SECONDS);
}
}
运行结果:
4、SingleThreadExecutor
Executors.newSingleThreadExecutor() 创建一个单线程的线程池,它只会用唯一的工作线程来执行任务,保证所有的任务会按照指定的顺序FIFO LIFO 优先级执行
package com.example.demothread.util;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @author lenovo
* @version 1.0
* @Date 2022/5/1 15:13
* @Description
*/
@Slf4j
public class MainDemo {
public static void main(String args[]) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for(int i=0;i<10;i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
try{
Thread.sleep(1000);
log.info(Thread.currentThread().getName());
} catch ( Exception ex) {
log.error("捕获到异常---{}",ex.getMessage(),ex);
}
}
});
}
}
}
运行结果:
6、缓冲队列BlockingQueue
blockingQueue是双缓冲队列,BlockingQueue 内部使用了两条队列, 允许两个线程同时向队列一个存储,一个取出操作,在保证并发安全的同时,提高了队列的存储效率
常见的几种blockingQueue
1、ArrayBlockingQueue( int i)
规定大小的blockingQueue ,其构造是指定了大小
2、LinkBlockingQUeue()
大小不固定,但是有顺序
3、PriorityBlockingQueue()
其对象的先后顺序是由构造函数的Comparator决定的
4、SysnchronizedQueue()
对其的操作必须是放和取交替完成的
二、Spring实现线程池
在Springboot 中对线程池进行了简化处理, 只需要配置一个java.util.concurrent.TaskExecutor 或者其子类的bean,并在配置类或者直接在程序入口类声明注解 @EnableAsync
调用异步的方法可以由Spring管理的对象方法上标注注解@Async
一般使用Spring 提供的ThreadPoolTaskExecutor 类作为线程池对象。
1、创建线程池的配置类
package com.example.demothread.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author lenovo
* @version 1.0
* @Date 2022/5/2 16:18
* @Description
*/
@Configuration
public class ExecutorConfig {
@Value("${threadPool.coreSize}")
private int corePoolSize;
@Value("${threadPool.maxSize}")
private int maxPoolSize;
@Value("${threadPool.queueSize}")
private int queueCapacity;
@Bean
public Executor asyncServiceExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
// rejection-policy 当pool 已经达到max size的时候 如何处理新的任务
// CAll_RUNS 不在新线程中执行任务,而是在调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 执行初始化
executor.initialize();
return executor;
}
}
server:
port: 8080
threadPool:
coreSize: 5 #核心线程数
maxSize: 10 #最大核心线程数
queueSize: 10 #队列容量
启动类
package com.example.demothread;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@EnableAsync
@SpringBootApplication
public class DemothreadApplication {
public static void main(String[] args) {
SpringApplication.run(DemothreadApplication.class, args);
}
}
2、使用类
这里为了更好的说明异步的效果,使用了Controller和service
package com.example.demothread.controller;
import com.example.demothread.service.TestService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author lenovo
* @version 1.0
* @Date 2022/5/2 16:28
* @Description
*/
@Slf4j
@RestController
public class TestController {
@Resource
private TestService testService;
@GetMapping("/testAsync")
public void testAsync() {
testService.testAsync();
log.info("事件结束的事件为---{}",System.currentTimeMillis());
}
}
接口
package com.example.demothread.service;
public interface TestService {
/**
* 测试异步
*/
void testAsync();
}
接口实现类
package com.example.demothread.service.impl;
import com.example.demothread.service.TestService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
/**
* @author lenovo
* @version 1.0
* @Date 2022/5/2 16:29
* @Description
*/
@Slf4j
@Service
public class TestServiceImpl implements TestService {
@Async
@Override
public void testAsync() {
try{
Thread.sleep(3000);
}catch (Exception ex) {
log.error("捕获到异常");
}
log.info("异步事件结束时间为---{}",System.currentTimeMillis());
}
}
运行效果:
注意:
异步方法使用@Async的返回值,只能为void 或者Future
由于@Async注解的实现都是基于Spring的Aop , 而Aop 的实现基于动态代理模式实现的,所以调用标注@Async方法的对象不能为本身
当然也可以在类里面直接定义一个线程池来实现
ThreadPoolTaskExecutor
@Slf4j
@Service
public class TestServiceImpl implements TestService {
@Value("${threadPool.coreSize}")
private int corePoolSize;
@Value("${threadPool.maxSize}")
private int maxPoolSize;
// 核心线程数、最大线程数为taskImpSize,无界队列
private ThreadPoolExecutor dealTaskThreadPool;
@PostConstruct
void generateDealTaskThreadPool() {
dealTaskThreadPool = new ThreadPoolExecutor(corePoolSize, maxPoolSize, 5, TimeUnit.MINUTES, new LinkedBlockingQueue<>());
}
}
三、Future
之前在写java 基本线程的文章的时候说过Thread 实现有三种方式,其中第三种就是使用Callable + FutureTask 获取到多线程返回的值
Spring中使用通过 AsynResult来获取线程返回的结果
package com.example.demothread.controller;
import com.example.demothread.service.TestService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* @author lenovo
* @version 1.0
* @Date 2022/5/2 16:28
* @Description
*/
@Slf4j
@RestController
public class TestController {
@Resource
private TestService testService;
@GetMapping("/testAsync")
public void testAsync() {
testService.testAsync();
log.info("事件结束的事件为---{}",System.currentTimeMillis());
}
@GetMapping("/testReturnAsync")
public void testReturnAsync() {
Future<String> result = testService.testReturnAsync();
try {
log.info("事件结束的结果为---{}",result.get(5000, TimeUnit.SECONDS));
} catch (Exception ex) {
log.error("捕获到异常---{}",ex.getMessage(),ex);
}
log.info("事件结束的时间为---{}",System.currentTimeMillis());
}
}
package com.example.demothread.service;
import java.util.concurrent.Future;
public interface TestService {
/**
* 测试异步
*/
void testAsync();
/**
* 存在返回结果的异步
* @return
*/
Future<String> testReturnAsync();
}
package com.example.demothread.service.impl;
import com.example.demothread.service.TestService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import java.util.concurrent.Future;
/**
* @author lenovo
* @version 1.0
* @Date 2022/5/2 16:29
* @Description
*/
@Slf4j
@Service
public class TestServiceImpl implements TestService {
@Async
@Override
public void testAsync() {
try{
Thread.sleep(3000);
}catch (Exception ex) {
log.error("捕获到异常");
}
log.info("异步事件结束时间为---{}",System.currentTimeMillis());
}
@Async
@Override
public Future<String> testReturnAsync() {
try{
Thread.sleep(3000);
}catch (Exception ex) {
log.error("捕获到异常");
}
log.info("异步事件结束时间为---{}",System.currentTimeMillis());
return new AsyncResult<>("异步线程已经处理完毕");
}
}
运行结果:
注意future.get是阻塞的,需要用到Future接口中的isDone() 方法来判断任务是否执行完成,如果执行完成则可以获取结果,如果没有完成则需要等待,可见虽然主线程的中的多个任务是异步执行的,但是无法确定任务什么时候执行完成,只能通过不断去监听以获取结果,所以这里是阻塞的,这样,可能某一个任务执行很长时间会拖累整个主任务的执行
所以可以使用Guava Future 能够减少主函数的等待时间,能够使得多任务异步非阻塞执行
ListenableFuture 是可以监听的FUture,它对java原生的Future的扩展增强,Future表示一个异步计算任务,当任务完成的时候可以得到计算结果。如果使用ListenableFuture,Guava 会帮助Future 是否完成了,如果完成了就自动调用回调函数,这样可以减少并发程序的复杂度
package com.example.demothread.util;
import com.google.common.util.concurrent.*;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* @author lenovo
* @version 1.0
* @Date 2022/5/1 15:13
* @Description
*/
@Slf4j
public class MainDemo {
public static final ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
public static void main(String args[]) {
long start = System.currentTimeMillis();
ListenableFuture<Boolean> booleanListenableFuture = service.submit(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
Thread.sleep(10000);
return true;
}
});
Futures.addCallback(booleanListenableFuture, new FutureCallback<Boolean>() {
@Override
public void onSuccess(@Nullable Boolean aBoolean) {
log.info("booleanListenableFuture---执行完毕--{}",aBoolean);
}
@Override
public void onFailure(Throwable throwable) {
log.info("booleanListenableFuture---执行异常--{}",throwable);
}
},service);
ListenableFuture<String> stringListenableFuture = service.submit(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(3000);
return "Hello World";
}
});
Futures.addCallback(stringListenableFuture, new FutureCallback<String>() {
@Override
public void onSuccess(@Nullable String s) {
log.info("StringTask 任务2-3s--{}",s);
}
@Override
public void onFailure(Throwable throwable) {
log.error("异步结果异常----{}",throwable);
}
},service);
ListenableFuture<Integer> integerListenableFuture = service.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(2000);
return new Random().nextInt(100);
}
});
Futures.addCallback(integerListenableFuture, new FutureCallback<Integer>() {
@Override
public void onSuccess(@Nullable Integer integer) {
log.info("IntegerTask 任务2-3s---{}",integer);
}
@Override
public void onFailure(Throwable throwable) {
log.error("捕获到异常---{}",throwable);
}
},service);
log.info("time---{}",System.currentTimeMillis()- start);
}
}
执行结果:
每个任务下面,都去获取任务的结果,从代码来看,任务1执行的时间是10s,任务2是3s,任务3s是2s, 虽然代码里面是任务1先执行结果的,但是从打印结果来看,是执行时间最少的先打印,执行时间最长的最后打印,说明它获取结果时,只要有结果返回,就能获取到,因为它是非阻塞的
四、CompletableFuture
CompletableFut ure 是JDK1.8 版本新引入的类,使用了CompletionStage接口支持完成触发的函数和操作
之前future需要等待isDone 为true 才能知道任务跑完了,或者是使用get方法调用的时候出现阻塞。而使用completableFuture的使用就可以用then,when 等等操作来防止以上的阻塞和轮询isDone的现象出现
package com.example.demothread.util;
import com.google.common.util.concurrent.*;
import com.sun.org.apache.bcel.internal.generic.ARETURN;
import com.sun.org.apache.xpath.internal.objects.XNull;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Random;
import java.util.concurrent.*;
/**
* @author lenovo
* @version 1.0
* @Date 2022/5/1 15:13
* @Description
*/
@Slf4j
public class MainDemo {
public static void main(String args[]) {
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
log.info("是否为守护进程---{}",Thread.currentThread().isDaemon());
return null;
}
});
final CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
log.info("this is lambda supplyAsync");
log.info("supplyAsync 是否为守护线程---{}",Thread.currentThread().isDaemon());
try{
TimeUnit.SECONDS.sleep(2);
} catch (Exception ex) {
log.error("捕获到异常---{}",ex.getMessage(),ex);
}
return "result";
});
final CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
log.info("this is task with executor");
log.info("supplyAsync 使用executorSerivce 是否为守护进程:---{}",Thread.currentThread().isDaemon());
return "result2";
},executorService);
try {
log.info(completableFuture.get());
log.info(future.get());
} catch (Exception ex) {
log.error("捕获到异常---{}",ex.getMessage(),ex);
}
executorService.shutdown();
}
}
运行结果:
这些任务中,带有supply是持有返回值的,run 是void 返回值的。
2、 allof 和 anyOf
这两个方法的入参是一个completableFuture组、allof 就是所有任务都完成时返回,但是是个void的返回值
anyOf 是当入参的completeableFuture 组中有一个任务执行完毕就返回,返回结果是第一个完成的任务的结果
package com.example.demothread.util;
import com.google.common.util.concurrent.*;
import com.sun.org.apache.bcel.internal.generic.ARETURN;
import com.sun.org.apache.xpath.internal.objects.XNull;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Random;
import java.util.concurrent.*;
/**
* @author lenovo
* @version 1.0
* @Date 2022/5/1 15:13
* @Description
*/
@Slf4j
public class MainDemo {
public static void main(String args[]) {
final CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync( () -> {
try {
Thread.sleep(3000);
} catch (Exception ex) {
log.error("捕获到异常---{}",ex.getMessage(),ex);
}
return "completableFuture";
});
final CompletableFuture<String> futureTwo = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(6000);
} catch (Exception ex) {
log.error("捕获到 异常---{}",ex.getMessage(),ex);
}
return "futureTwo";
});
CompletableFuture future = CompletableFuture.allOf(completableFuture,futureTwo);
try {
log.info("全部完成--{}",future.get());
} catch (Exception ex) {
log.error("结果异常---{}",ex.getMessage(),ex);
}
CompletableFuture completableFuture1 = CompletableFuture.anyOf(completableFuture,futureTwo);
try {
completableFuture1.get();
} catch (Exception ex) {
log.error("捕获到异常---{}",ex.getMessage(),ex);
}
}
}
3 thenCombine
就是结合两个任务的结果
package com.example.demothread.util;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.*;
/**
* @author lenovo
* @version 1.0
* @Date 2022/5/1 15:13
* @Description
*/
@Slf4j
public class MainDemo {
public static void main(String args[]) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() ->{
return "first";
});
try {
Thread.sleep(1000);
} catch (Exception ex) {
log.error("捕获到异常---{}",ex.getMessage(),ex);
}
CompletableFuture<String> combine = future.thenCombine(CompletableFuture.supplyAsync(() -> {
log.info("next step");
try {
Thread.sleep(100);
} catch (Exception ex) {
log.error("捕获到异常---{}",ex.getMessage(),ex);
}
return "second";
}),(s1,s2) -> {
log.info(Thread.currentThread().getName());
return s1 + s2;
});
try {
log.info(combine.get(4000,TimeUnit.SECONDS));
} catch (Exception ex) {
log.error("捕获到异常");
}
}
}
4、whenComplete whenCompleteAsync
whenComoplete 是任务执行完毕之后调用,传入一个action,这个方法的执行线程是当前线程,意味着会阻塞当前线程
whenCompoleteAsync 这个就是新创建有一个异步线程执行,所以不会阻塞
package com.example.demothread.util;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.*;
/**
* @author lenovo
* @version 1.0
* @Date 2022/5/1 15:13
* @Description
*/
@Slf4j
public class MainDemo {
public static void main(String args[]) {
CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("this is first task");
return "first";
});
completableFuture.whenCompleteAsync((s,e) ->{
try {
Thread.sleep(1000);
} catch (Exception ex) {
log.error("捕获到异常---{}",ex.getMessage(),ex);
}
System.out.println("输出到正确的结果---"+s);
System.out.println("e------"+e);
});
System.out.println("test");
try {
Thread.sleep(1000);
} catch (Exception ex) {
log.error("捕获到异常");
}
}
}
运行结果:
从结果中可以看出,当前线程并没有被阻塞
而Completable 在一定情况下会阻塞线程
package com.example.demothread.util;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.*;
/**
* @author lenovo
* @version 1.0
* @Date 2022/5/1 15:13
* @Description
*/
@Slf4j
public class MainDemo {
public static void main(String args[]) {
CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("this is first task");
try {
Thread.sleep(100);
log.info("CompletableFuture-----{}",Thread.currentThread());
} catch (Exception ex) {
log.error("捕获到异常---{}",ex.getMessage(),ex);
}
return "first";
});
completableFuture.whenComplete((s, e) -> {
try {
log.info("whenCompletable---{}",Thread.currentThread());
Thread.sleep(1000);
} catch (Exception ex) {
log.error("捕获到异常---{}", ex.getMessage(), ex);
}
System.out.println("输出到正确的结果---" + s);
System.out.println("e------" + e);
});
System.out.println("test");
try {
Thread.sleep(1000);
} catch (Exception ex) {
log.error("捕获到异常");
}
}
}
运行结果:
package com.example.demothread.util;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.*;
/**
* @author lenovo
* @version 1.0
* @Date 2022/5/1 15:13
* @Description
*/
@Slf4j
public class MainDemo {
public static void main(String args[]) {
CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("this is first task");
try {
Thread.sleep(100);
System.out.println("CompletableFuture-----"+Thread.currentThread());
} catch (Exception ex) {
ex.printStackTrace();
}
return "first";
});
try {
Thread.sleep(400);
} catch (Exception ex) {
log.error("捕获到异常");
}
completableFuture.whenComplete((s, e) -> {
try {
System.out.println("whenCompletable---{}"+Thread.currentThread());
Thread.sleep(100);
} catch (Exception ex) {
ex.printStackTrace();
}
System.out.println("输出到正确的结果---" + s);
System.out.println("e------" + e);
});
System.out.println("test");
try {
Thread.sleep(1000);
} catch (Exception ex) {
log.error("捕获到异常");
}
}
}