zoukankan      html  css  js  c++  java
  • spring学习(二十八)--@Async线程池

    之前使用java线程池,都是先自己通过实现ThreadPoolExecutor写线程池类,然后写多线程类,用线程池的execute方法去执行多线程类类。

    在spring中,可以通过@Async(value="beanId")注解来使用线程池进行多线程编程。

    线程池的创建有两种方式,一种是配置文件,一种是通过java编程。

    1、通过配置文件创建线程池,在spring配置文件中加入下面配置,配置文件要开启多线程类所在包的注解扫描:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:task="http://www.springframework.org/schema/task"
           xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
         http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
         http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
         http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"
           default-autowire="byName">
    
        <description>我的spring线程池配置</description>
    
        <!-- 配置@Async注解缺省参数时候的默认异步任务线程池为myExecutor -->
        <task:annotation-driven executor="myExecutor"/>

      <!-- 第一个线程池 --> <task:executor id="asyncExecutor" pool-size="100-10000" queue-capacity="10"/> <!-- 第二个线程池 --> <task:executor id="myExecutor" pool-size="15-50" queue-capacity="100" keep-alive="60" rejection-policy="CALLER_RUNS"/> </beans>

    <task:executor />配置参数:

    id:当配置多个executor时,被@Async("id")指定使用;也被作为线程名的前缀。

    pool-size:格式"core size-max size"

    core size:最小的线程数,缺省:1

    max size:最大的线程数,缺省:Integer.MAX_VALUE

    queue-capacity:当最小的线程数已经被占用满后,新的任务会被放进queue里面,当这个queue的capacity也被占满之后,pool里面会创建新线程处理这个任务,直到总线程数达到了max size,这时系统会拒绝这个任务并抛出TaskRejectedException异常(缺省配置的情况下,可以通过rejection-policy来决定如何处理这种情况)。缺省值为:Integer.MAX_VALUE

    keep-alive:超过core size的那些线程,任务完成后,再经过这个时长(秒)会被结束掉

    rejection-policy:当pool已经达到max size的时候,如何处理新任务

    ABORT(缺省):抛出TaskRejectedException异常,然后不执行DISCARD:不执行,也不抛出异常

    DISCARD_OLDEST:丢弃queue中最旧的那个任务

    CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行

    在方法中加上@Async注解

    package cn.leadeon.message.test;
    
    import org.springframework.context.annotation.ImportResource;
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.stereotype.Component;
    
    @Component
    @ImportResource("classpath:/config/spring-threadpool.xml")
    public class AsyncTest {
    
       //这个方法注解缺省了value参数,使用默认的myExecutor作为线程池 @Async
    public void test1() { System.out.println("异步执行test1!!!"); System.out.println("线程id:" + Thread.currentThread().getId()); System.out.println("线程名称:" + Thread.currentThread().getName()); }
       
      //这个方法有参数(value="asyncExecutor"),使用asyncExecutor作为线程池
      @Async(value="asyncExecutor")
        public void test2() {
            System.out.println("异步执行test2!!!");
            System.out.println("线程id:" + Thread.currentThread().getId());
            System.out.println("线程名称:" + Thread.currentThread().getName());
        }
    }

    当在外部调用testAsync方法时即在新线程中执行,由上面<task: annotation-driven/>执行器去维护线程。

    总结:先用context:component-scan去扫描注解,让spring能识别到@Async注解,然后task:annotation-driven去驱动@Async注解,并可以指定默认的线程执行器executor。那么当用@Async注解的方法或类得到调用时,线程执行器会创建新的线程去执行。

    2、注意点:@EnableAsync注解与<task:annotation-driven executor="messageExecutor"/>等价,两者只能使用其一,不然启动会报错

    新建一个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时候的处理方法;同时指定了线程名字的前缀。

    编写多线程类使用线程池:

    @Component
    public class AsyncTaskDemo4 {
        protected final Logger logger = LoggerFactory.getLogger(this.getClass());
     
        @Async("mySimpleAsync")
        public Future<String> 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<String> 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!");
        }
    }

    测试结果:

    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'

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

    个人理解,如有错误,欢迎指正!
  • 相关阅读:
    【游戏开发】Excel表格批量转换成CSV的小工具
    iOS
    iOS
    Xcode
    iOS
    iOS
    iOS
    iOS
    iOS
    iOS
  • 原文地址:https://www.cnblogs.com/gllegolas/p/11817234.html
Copyright © 2011-2022 走看看