• 调用接口,异步处理业务


    调用接口时,如果后台需要处理的时间过长,需要采取异步处理,先把结果返回给前台。

    1、原生的

    接口定义:

    @RequestMapping(value="/test")
        public Object test(){
             MyExecutor  myExecutor = new MyExecutor();
             try {
                myExecutor.work();
            }catch(Exception e) {
                System.out.println("myExecutor.work()"+e.getMessage());
            }
             System.out.println("返回结果:  "+new Date());
            return "成功";
        }

    业务执行:

    import java.util.Date;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class MyExecutor {
    
        private ExecutorService executor = Executors.newCachedThreadPool() ;
    
        public void work() throws Exception {
            executor.submit(new Runnable(){
                public void run() {
                    try {
                        System.out.println("开始处理业务。。。    "+new Date());
                        Thread.sleep(5000);
                        System.out.println("业务结束。。。   "+new Date());
                    }catch(Exception e) {
                        System.out.println(e.getMessage());
                    }
                }
            });
        }
    
    }

    控制台输出:

    返回结果:  Sat Jul 27 09:28:33 GMT+08:00 2019
    开始处理业务。。。    Sat Jul 27 09:28:33 GMT+08:00 2019
    业务结束。。。   Sat Jul 27 09:28:38 GMT+08:00 2019

    2、在springboot  中使用

    controller层

    @RequestMapping(value="/async2")
        public Object testAsync2(){
            System.out.println(1111);
            userService.testAsync();
            System.out.println(12);
            return "asdfas";
        }

    异步方法加注解    @Async (org.springframework.scheduling.annotation.Async;) 

        @Override
        @Async
        public void testAsync() {
            System.out.println("开始异步处理业务。。");
            try {
                Thread.sleep(5000);
                System.out.println("结束。。。。。");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    启动类加注解  @EnableAsync  开启异步支持

    扩展:

    异步方法有返回值

    @Async
        public Future<String> asyncMethodWithReturnType() {
             System.out.println("Execute method asynchronously - "+ Thread.currentThread().getName());
             try {
                 Thread.sleep(5000);
                 return new AsyncResult<String>("hello world !!!!");
             } catch (InterruptedException e) {
                 //
             }
         
             return null;
        }

    异步操作的执行器

    默认情况下,Spring 使用SimpleAsyncTaskExecutor去执行这些异步方法(此执行器没有限制线程数)。此默认值可以从两个层级进行覆盖:

    1. 方法级别覆盖

    @Async("threadPoolTaskExecutor")
        public void asyncMethodWithConfiguredExecutor() {
            System.out.println("Execute method with configured executor - "+ Thread.currentThread().getName());
        }

    2. 应用级别覆盖

    配置类应该实现AsyncConfigurer接口——这意味着它拥有getAsyncExecutor()方法的实现。在这里,我们将返回整个应用程序的执行器——这现在成为运行带有@Async注释的方法的默认执行器:(异步中的线程名称-- ThreadPoolTaskExecutor-1 ,与所使用的执行相关)

    import java.util.concurrent.Executor;
    
    import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
    import org.springframework.scheduling.annotation.AsyncConfigurer;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    
    @Configuration @EnableAsync
    public class SpringAsyncConfig implements AsyncConfigurer{ @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.initialize(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(25); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new CustomAsyncExceptionHandler(); } }
    CustomAsyncExceptionHandler 是一个自定义的异常捕捉,当方法返回值是Future的时候,异常捕获是没问题的 - Future.get()方法会抛出异常。但是,如果返回类型是Void,那么异常在当前线程就捕获不到。因此,我们需要添加额外的配置来处理异常。
    通过实现AsyncUncaughtExceptionHandler接口创建一个定制的async异常处理程序。handleUncaughtException()方法在存在任何未捕获的异步异常时调用:
    import java.lang.reflect.Method;
    
    import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
    
    public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
    
        @Override
        public void handleUncaughtException(
                Throwable throwable, Method method, Object... obj) {
            System.out.println("Exception message - " + throwable.getMessage());
            System.out.println("Method name - " + method.getName());
            for (Object param : obj) {
                System.out.println("Parameter value - " + param);
            }
    
        }
    
    }

    3、spring MVC 中使用

    需要加配置

    相关的配置:
    <task:annotation-driven />配置:
    • executor:指定一个缺省的executor给@Async使用。
    例子:
    <task:annotation-driven executor="asyncExecutor" />
     
    <task:executor />配置参数:
    • id:当配置多个executor时,被@Async("id")指定使用;也被作为线程名的前缀。
    • pool-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:不在新线程中执行任务,而是有调用者所在的线程来执行

    配置例子:

    <task:annotation-driven executor="asyncExecutor" />
     <task:executor id="asyncExecutor" pool-size="100-10000" queue-capacity="10"/>

    实例:

    <!-- 缺省的异步任务线程池 -->   
    <task:annotation-driven executor="asyncExecutor" />  
    <task:executor id="asyncExecutor" pool-size="100-10000" queue-capacity="10" />  
      
    <!-- 处理log的线程池 -->  
    <task:executor id="logExecutor" pool-size="15-1000" queue-capacity="5" keep-alive="5"/>  
    @Override  
    @Async("logExecutor")    //如果不指定名字,会使用缺省的“asyncExecutor”  
    public void saveUserOpLog(TabUserOpLog tabUserOpLog) {  
      
     userOpLogDAO.insertTabUserOpLog(tabUserOpLog);  
    }

    (注意:如果在同一个类中调用的话,不会生效,原因请参考:http://blog.csdn.net/clementad/article/details/47339519

    线程的优先级和类型:
    优先级:NORM_PRIORITY
    类型:非守护线程
     
    用户线程(User Thread):JVM会等待所有的用户线程结束后才退出;当系统中没有用户线程了,JVM也就退出了
    守护线程(Daemon Thread):一般是为其他线程提供服务的线程,比如GC垃圾回收器;JVM退出时,不会管守护线程是否存在,而是直接退出
    所以,对于文件、数据库的操作,不适宜使用守护线程,不然可能会丢失数据!
     
    Web应用停止时,Spring容器会被关闭,调用者如果是Spring bean,就会停止生成新任务。然而,线程池中已经在运行的任务,由于缺省是用户线程,所以JVM会等待它们结束后才退出。

     附:Java编程方式的配置方法:

    @Configuration  
    @EnableAsync  
    public class SpringConfig {  
      
        /** 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;  
      
        private String ThreadNamePrefix = "MyLogExecutor-";  
      
        @Bean  
        public Executor logExecutor() {  
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();  
            executor.setCorePoolSize(corePoolSize);  
            executor.setMaxPoolSize(maxPoolSize);  
            executor.setQueueCapacity(queueCapacity);  
            executor.setThreadNamePrefix(ThreadNamePrefix);  
      
            // rejection-policy:当pool已经达到max size的时候,如何处理新任务  
            // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行  
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());  
            executor.initialize();  
            return executor;  
        }  
      
    }  

    参考:  https://www.cnblogs.com/panxuejun/p/7838970.html

    https://blog.csdn.net/xiaoxiaole0313/article/details/104666789

    SimpleAsyncTaskExecutor

  • 相关阅读:
    JavaScript设计模式-21.命令模式
    JavaScript设计模式-20.责任链模式
    JavaScript设计模式-18.享元模式
    JavaScript设计模式-19.代理模式
    JavaScript设计模式-17.装饰者模式(下)
    JavaScript设计模式-16.装饰者模式(上)
    面向对象之集合ArrayList
    面向对象之继承
    字符串的添加与切割~~~
    面向对象中构造函数的小练习
  • 原文地址:https://www.cnblogs.com/yrjns/p/11253932.html
Copyright © 2020-2023  润新知