springboot实现Java异步线程的几种方法

我们讲述几种比较实用的,平时写代码可能用到的方法

1 以下这几种实际写代码不会用到

1.1继承Thread类 

1.2 实现Runable接口

1.3 实现Callable 接口

上述三种都是使用 接口或父类中的 start() 方法来实现接口的异步执行, 不过平时写代码几乎不会用到, 因为太原始了 ,只有面试的时候可能才用得到.

2 使用springboot中的@Async注解

@Async注解实际上采用的是动态代理进行异步实现, 所以该注解需要放在spring管理的Bean上.

2.1 直接使用

将注解直接标注在spring管理的类中的方法上

@Async()
    @Override
    public void sout1() {
        try {
            Thread.sleep(3000);
            log.info(Thread.currentThread().getName());
            System.out.println("当前时间为:" + simpleDateFormat.format(new Date()));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

2.2 指定线程池

如下方法指定了线程池 sout2

 @Async("sout2")
    @Override
    public void sout2() {
        try {
            Thread.sleep(3000);
            log.info(Thread.currentThread().getName());
            System.out.println("当前时间为:" + dateTimeFormatter.format(LocalDateTime.now()));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

线程池可以按照如下配置

@Configuration
@EnableAsync
public class ExecutorConfig {
    /**
     * 线程池维护线程的最少数量
     */
    private final int corePoolSize = 1;
    /**
     * 线程池维护线程的最大数量
     */
    private final int maxPoolSize = 1;
    /**
     * 缓存队列
     */
    private final int queueCapacity = 1;
    /**
     * 允许的空闲时间,单位秒(S)
     */
    private final int keepAlive = 30;

    @Bean("sout2")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setThreadNamePrefix("boot-executor-sout2-");
        executor.setKeepAliveSeconds(keepAlive);
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.initialize();
        return executor;

    }

2.3 有返回值的使用

可以使用 Future接收异步线程的返回值, 这表示你需要该异步线程执行完毕后的结果

     假如使用了get() 方法, 总线程会陷入阻塞,只有等到使用get()方法的线程执行完毕才会继续进行.

也就是说即使是你的异步接口有返回值,但是你并没有调用 get()方法,主线程并不会阻塞,会按照原步骤继续执行. 

 @RequestMapping(value = "/ex3", method = RequestMethod.GET)
    public Object sout3(HttpServletRequest request, HttpServletResponse response) throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        Future<String>  String3 = testService.sout3();
        Future<String>  String4 = testService.sout4();
        Future<String>  String5 = testService.sout5();
        System.out.println(" 执行完毕ex3");
        String3.get();
        String4.get();
        String5.get();
        //logger.info("111:{}---222:{}---333:{}",String3.get(),String4.get(),String5.get());
        logger.info("执行时间为:{}",(System.currentTimeMillis()-start)/1000);
        return null;
    }

/*******/
......
/*******/

   @Override
    @Async("sout2")
    public Future<String> sout3() {
        try {
            Thread.sleep(3000);
            //log.info("sout3方法执行1======>>>{}",Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return AsyncResult.forValue("sout3");
    }

2.4注意事项

因为使用了动态代理, 所以你在同一个类中调用并不会异步执行,所以不可以放在同一个类中

异步方法不允许使用static 修饰

@Async不可以与 @Transactional 注解出现在同一个类中

3 使用Future

CompletableFuture 是 java.util.concurrent包中提供的类

CompletableFuture<T> aFuture = CompletableFuture.supplyAsync(() -> {
      return T;
}, threadPool);

// 以下方法会使用容器自带的线程池
//CompletableFuture<T> aFuture = CompletableFuture.supplyAsync(() -> {
//      return T;
//});

// CompletableFuture.supplyAsync(() -> {
//      return T;
//}, threadPool).get();


CompletableFuture allFuture = CompletableFuture.allOf(aFuture, bFuture, vFuture, propertyFuture, dFuture,eFuture, fFuture);
allFuture.get();


当CompletableFuture全部线程执行完毕后, 主线程才会继续执行.

4 使用 ListeningExecutorService

引入依赖

 <!--guava异步-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>28.2-jre</version>
        </dependency>
// 可以使用自己配置的线程池 上面有配置示例
// @autowired
// private ThreadPoolTaskExecutor threadPoolTaskExecutor
// 也可以自己new 缓存线程
ExecutorService threadPool = Executors.newCachedThreadPool();
ListeningExecutorService service = MoreExecutors.listeningDecorator(threadPool);


 ListenableFuture<T> t= service.submit(() -> { return T;});

t.get();


get() 方法实现的都是一样的, 将主线程阻塞,等待异步线程执行完毕.

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值