Spring Boot 自定义线程池使用@Async实现异步调用任务

时间:2020-07-10 18:41:19   收藏:0   阅读:69

定义线程池

第一步,先在Spring Boot主类中定义一个线程池,比如:

@SpringBootApplication
public class Application {
 
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
 
    @EnableAsync
    @Configuration
    class TaskPoolConfig {
 
        @Bean("taskExecutor")
        public Executor taskExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(10);
            executor.setMaxPoolSize(20);
            executor.setQueueCapacity(200);
            executor.setKeepAliveSeconds(60);
            executor.setThreadNamePrefix("taskExecutor-");
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            return executor;
        }
    }
 
}

上面我们通过使用ThreadPoolTaskExecutor创建了一个线程池,同时设置了以下这些参数:

使用线程池

在定义了线程池之后,我们如何让异步调用的执行任务使用这个线程池中的资源来运行呢?方法非常简单,我们只需要在@Async注解中指定线程池名即可,比如:

@Slf4j
@Component
public class Task {
 
    public static Random random = new Random();
 
    @Async("taskExecutor")
    public void doTaskOne() throws Exception {
        log.info("开始做任务一");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务一,耗时:" + (end - start) + "毫秒");
    }
 
    @Async("taskExecutor")
    public void doTaskTwo() throws Exception {
        log.info("开始做任务二");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务二,耗时:" + (end - start) + "毫秒");
    }
 
    @Async("taskExecutor")
    public void doTaskThree() throws Exception {
        log.info("开始做任务三");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务三,耗时:" + (end - start) + "毫秒");
    }
 
}

单元测试

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class ApplicationTests {
 
    @Autowired
    private Task task;
 
    @Test
    public void test() throws Exception {
 
        task.doTaskOne();
        task.doTaskTwo();
        task.doTaskThree();
 
        Thread.currentThread().join();
    }
 
}

执行上面的单元测试,我们可以在控制台中看到所有输出的线程名前都是之前我们定义的线程池前缀名开始的,说明我们使用线程池来执行异步任务的试验成功了!

2020-03-27 22:01:15.620  INFO 73703 --- [ taskExecutor-1] com.didispace.async.Task                 : 开始做任务一
2020-03-27 22:01:15.620  INFO 73703 --- [ taskExecutor-2] com.didispace.async.Task                 : 开始做任务二
2020-03-27 22:01:15.620  INFO 73703 --- [ taskExecutor-3] com.didispace.async.Task                 : 开始做任务三
2020-03-27 22:01:18.165  INFO 73703 --- [ taskExecutor-2] com.didispace.async.Task                 : 完成任务二,耗时:2545毫秒
2020-03-27 22:01:22.149  INFO 73703 --- [ taskExecutor-3] com.didispace.async.Task                 : 完成任务三,耗时:6529毫秒
2020-03-27 22:01:23.912  INFO 73703 --- [ taskExecutor-1] com.didispace.async.Task                 : 完成任务一,耗时:8292毫秒

注意,方法标记了@Async  调用该方法,不能在该类中调用,不然不管用,就是调用者和被调用者不能是用一个类;

 Future类型

异步方法里按照平时那样返回结果,主线程是获取不到的;Future的get方法可以阻塞主线程,直到子线程执行完毕,获取异步结果;

 

Future提供了三种功能:

  1. 判断任务是否完成;

  2. 能够中断任务;

  3. 能够获取任务执行结果

    @Async("taskExecutor")
     
       public Future<String> run() throws Exception {
     
           long sleep = random.nextInt(10000);
     
           log.info("开始任务,需耗时:" + sleep + "毫秒");
     
           Thread.sleep(sleep);
     
           log.info("完成任务");
     
           return new AsyncResult<>("test");
     
       }
    @Test
     
       public void test() throws Exception {
     
           Future<String> futureResult = task.run();
     
           String result = futureResult.get(5, TimeUnit.SECONDS);
     
           log.info(result);
     
       }
     
     
    Future它的接口定义如下:
     
     
     
    public interface Future<V> {
     
       boolean cancel(boolean mayInterruptIfRunning);
     
       boolean isCancelled();
     
       boolean isDone();
     
       V get() throws InterruptedException, ExecutionException;
     
       V get(long timeout, TimeUnit unit)
     
           throws InterruptedException, ExecutionException, TimeoutException;
     
    }

     

 

它的接口定义如下:

public interface Future<V> {

   boolean cancel(boolean mayInterruptIfRunning);

   boolean isCancelled();

   boolean isDone();

   V get() throws InterruptedException, ExecutionException;

   V get(long timeout, TimeUnit unit)

       throws InterruptedException, ExecutionException, TimeoutException;

}

它声明这样的五个方法:

如果是用循环跑了多个任务,想要等待多个异步任务都结束,主线程才结束,可以这样写

List<Future<String>> futures = new ArrayList<>();
for(int i=0;i<companyNamejsonArray.size();i++){
        Future<String> future = financialCaseService.addDomList(companyNamejsonArray.getString(i), automaticTask.getTaskName(), taskId,i);
//在addDom中用return new AsyncResult<>(phoneNum+"成功");返回信息
        futures.add(future);
    }
 
for (Future future : futures) {
    String string = (String) future.get();
    System.out.println(string);
}

 

评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!