Java多线程——spring-boot 线程池 @Async 的使用、自定义Executor的配置方法

实现异步新线程调用,Springboot简单配置:

1、在主类中添加@EnableAsync注解:

@SpringBootApplication
@EnableAsync
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

2、创建一个AsyncTask类,在里面添加两个用@Async注解的task:

@Component
public class AsyncTask {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Async
    public Future doTask1() throws InterruptedException{
        logger.info("Task1 started.");
        long start = System.currentTimeMillis();
        Thread.sleep(5000);
        long end = System.currentTimeMillis();

        logger.info("Task1 finished, time elapsed: {} ms.", end-start);

        return new AsyncResult<>("Task1 accomplished!");
    }

    @Async
    public Future doTask2() throws InterruptedException{
        logger.info("Task2 started.");
        long start = System.currentTimeMillis();
        Thread.sleep(3000);
        long end = System.currentTimeMillis();

        logger.info("Task2 finished, time elapsed: {} ms.", end-start);

        return new AsyncResult<>("Task2 accomplished!");
    }
}

测试代码:注测试Junit版本4.12以上

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class BasicUtClass {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    protected final ObjectMapper objectMapper = new ObjectMapper();

}
public class TaskTests extends BasicUtClass {
    @Autowired
    private AsyncTask asyncTask;


    @Test
    public void AsyncTaskTest() throws InterruptedException, ExecutionException {
        Future task1 = asyncTask.doTask1();
        Future task2 = asyncTask.doTask2();

        while(true) {
            if(task1.isDone() && task2.isDone()) {
                logger.info("Task1 result: {}", task1.get());
                logger.info("Task2 result: {}", task2.get());
                break;
            }
            Thread.sleep(1000);
        }

        logger.info("All tasks finished.");
    }
}

运行结果:

2018-09-07 22:38:11.335  INFO 13908 --- [           main] .s.a.AnnotationAsyncExecutionInterceptor : More than one TaskExecutor bean found within the context, and none is named 'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly as an alias) in order to use it for async processing: [mySimpleAsync, myAsync]
2018-09-07 22:38:11.369  INFO 13908 --- [cTaskExecutor-1] com.work.spring.thread.demo3.AsyncTask   : Task1 started.
2018-09-07 22:38:11.369  INFO 13908 --- [cTaskExecutor-2] com.work.spring.thread.demo3.AsyncTask   : Task2 started.
2018-09-07 22:38:14.369  INFO 13908 --- [cTaskExecutor-2] com.work.spring.thread.demo3.AsyncTask   : Task2 finished, time elapsed: 3000 ms.
2018-09-07 22:38:16.369  INFO 13908 --- [cTaskExecutor-1] com.work.spring.thread.demo3.AsyncTask   : Task1 finished, time elapsed: 5000 ms.
2018-09-07 22:38:17.351  INFO 13908 --- [           main] com.work.spring.thread.TaskTests         : Task1 result: Task1 accomplished!
2018-09-07 22:38:17.351  INFO 13908 --- [           main] com.work.spring.thread.TaskTests         : Task2 result: Task2 accomplished!
2018-09-07 22:38:17.351  INFO 13908 --- [           main] com.work.spring.thread.TaskTests         : All tasks finished.

可以看到,没有自定义的Executor,所以使用缺省的TaskExecutor 。

自定义的Executor,可以按照如下几步来:

1、新建一个Executor配置类,顺便把@EnableAsync注解搬到这里来:

@Configuration
@EnableAsync
public class ExecutorConfig {
    /** Set the ThreadPoolExecutor's core pool size. */
    private int corePoolSize = 10;
    /** Set the ThreadPoolExecutor's maximum pool size. */
    private int maxPoolSize = 200;
    /** Set the capacity for the ThreadPoolExecutor's BlockingQueue. */
    private int queueCapacity = 10;

    @Bean
    public Executor mySimpleAsync() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setThreadNamePrefix("MySimpleExecutor-");
        executor.initialize();
        return executor;
    }

    @Bean
    public Executor myAsync() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setThreadNamePrefix("MyExecutor-");

        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

这里定义了两个不同的Executor,第二个重新设置了pool已经达到max size时候的处理方法;同时指定了线程名字的前缀。

2、自定义Executor的使用:

@Component
public class AsyncTaskDemo4 {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Async("mySimpleAsync")
    public Future doTask1() throws InterruptedException{
        logger.info("Task1 started.");
        long start = System.currentTimeMillis();
        Thread.sleep(5000);
        long end = System.currentTimeMillis();

        logger.info("Task1 finished, time elapsed: {} ms.", end-start);

        return new AsyncResult<>("Task1 accomplished!");
    }

    @Async("myAsync")
    public Future doTask2() throws InterruptedException{
        logger.info("Task2 started.");
        long start = System.currentTimeMillis();
        Thread.sleep(3000);
        long end = System.currentTimeMillis();

        logger.info("Task2 finished, time elapsed: {} ms.", end-start);

        return new AsyncResult<>("Task2 accomplished!");
    }
}

测试方法同上

测试ji结果:

2018-09-07 22:41:44.429  INFO 13640 --- [           main] com.work.spring.thread.TaskTests2        : Started TaskTests2 in 4.179 seconds (JVM running for 5.853)
2018-09-07 22:41:44.525  INFO 13640 --- [   MyExecutor-1] c.w.spring.thread.demo4.AsyncTaskDemo4   : Task2 started.
2018-09-07 22:41:44.525  INFO 13640 --- [impleExecutor-1] c.w.spring.thread.demo4.AsyncTaskDemo4   : Task1 started.
2018-09-07 22:41:47.526  INFO 13640 --- [   MyExecutor-1] c.w.spring.thread.demo4.AsyncTaskDemo4   : Task2 finished, time elapsed: 3001 ms.
2018-09-07 22:41:49.526  INFO 13640 --- [impleExecutor-1] c.w.spring.thread.demo4.AsyncTaskDemo4   : Task1 finished, time elapsed: 5001 ms.
2018-09-07 22:41:50.524  INFO 13640 --- [           main] com.work.spring.thread.TaskTests2        : Task1 result: Task1 accomplished!
2018-09-07 22:41:50.524  INFO 13640 --- [           main] com.work.spring.thread.TaskTests2        : Task2 result: Task2 accomplished!
2018-09-07 22:41:50.524  INFO 13640 --- [           main] com.work.spring.thread.TaskTests2        : All tasks finished.
2018-09-07 22:41:50.528  INFO 13640 --- [       Thread-3] ConfigServletWebServerApplicationContext : Closing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@747f281: startup date [Fri Sep 07 22:41:40 CST 2018]; root of context hierarchy
2018-09-07 22:41:50.532  INFO 13640 --- [       Thread-3] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'myAsync'
2018-09-07 22:41:50.532  INFO 13640 --- [       Thread-3] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'mySimpleAsync'

可见,线程名字的前缀变了,两个task使用了不同的线程池了。

扩展ThreadPoolTaskExecutor

虽然我们已经用上了线程池,但是还不清楚线程池当时的情况,有多少线程在执行,多少在队列中等待呢?这里我创建了一个ThreadPoolTaskExecutor的子类,在每次提交线程的时候都会将当前线程池的运行状况打印出来,代码如下:

public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
    private static final Logger logger = LoggerFactory.getLogger(VisiableThreadPoolTaskExecutor.class);

    private void showThreadPoolInfo(String prefix){
        ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();

        if(null==threadPoolExecutor){
            return;
        }

        logger.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
                this.getThreadNamePrefix(),
                prefix,
                threadPoolExecutor.getTaskCount(),
                threadPoolExecutor.getCompletedTaskCount(),
                threadPoolExecutor.getActiveCount(),
                threadPoolExecutor.getQueue().size());
    }

    @Override
    public void execute(Runnable task) {
        showThreadPoolInfo("1. do execute");
        super.execute(task);
    }

    @Override
    public void execute(Runnable task, long startTimeout) {
        showThreadPoolInfo("2. do execute");
        super.execute(task, startTimeout);
    }

    @Override
    public Future submit(Runnable task) {
        showThreadPoolInfo("1. do submit");
        return super.submit(task);
    }

    @Override
    public  Future submit(Callable task) {
        showThreadPoolInfo("2. do submit");
        return super.submit(task);
    }

    @Override
    public ListenableFuture submitListenable(Runnable task) {
        showThreadPoolInfo("1. do submitListenable");
        return super.submitListenable(task);
    }

    @Override
    public  ListenableFuture submitListenable(Callable task) {
        showThreadPoolInfo("2. do submitListenable");
        return super.submitListenable(task);
    }
}

如上所示,showThreadPoolInfo方法中将任务总数、已完成数、活跃线程数,队列大小都打印出来了,然后Override了父类的execute、submit等方法,在里面调用showThreadPoolInfo方法,这样每次有任务被提交到线程池的时候,都会将当前线程池的基本情况打印到日志中;

修改ExecutorConfig.java的asyncServiceExecutor方法,将ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor()改为ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor(),如下所示:

 //使用VisiableThreadPoolTaskExecutor
ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();

线程任务总数、已完成数、活跃线程数,队列大小等情况一目了然。

git项目地址

你可能感兴趣的:(JAVA,JAVA并发编程)