• SpringBoot(一) 多线程与异步


    多线程与异步

    异步是目的,而多线程是实现这个目的的方法。

    1 Java J.U.C线程调度

    JDK 1.5新增的java.util.concurrent包,增加了并发编程的很多类。

    image-20200707230102829

    Executor

    定义了方法execute(),用来执行一个任务

    public interface Executor {
        void execute(Runnable command);
    }
    
    ExecutorService

    提供了生命周期管理的方法。

    submit()

    shutdown()

    invokeAll()

    invokeAny()

    ThreadPoolExecutor

    Java提供的线程池类。可自定义线程池大小及线程处理机制。

    corePoolSize:线程池的基本大小,即在没有任务需要执行的时候线程池的大小,并且只有在工作队列满了的情况下才会创建超出这个数量的线程。

    maximumPoolSize:线程池中允许的最大线程数,线程池中的当前线程数目不会超过该值。

    keepAliveTime:线程空闲时间

    poolSize表示当前线程数,新提交一个任务时的处理流程为:

    • 如果当前线程池的线程数还没有达到基本大小(poolSize < corePoolSize),无论是否有空闲的线程新增一个线程处理新提交的任务

    • 如果当前线程池的线程数大于或等于基本大小(poolSize >= corePoolSize) 且任务队列未满时,就将新提交的任务提交到阻塞队列排队,等候处理。

    • 如果当前线程池的线程数大于或等于基本大小(poolSize >= corePoolSize) 且任务队列满时:

      • 当前poolSize<maximumPoolSize,那么就新增线程来处理任务
      • 当前poolSize=maximumPoolSize,那么意味着线程池的处理能力已经达到了极限,此时需要拒绝新增加的任务。至于如何拒绝处理新增的任务,取决于线程池的饱和策略RejectedExecutionHandler。
    ScheduledExecutorService

    定时调度接口。有4种调度机制:

    //带延迟时间的调度,只执行一次,调度之后可通过Future.get()阻塞直至任务执行完毕
    public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);
    //带延迟时间的调度,只执行一次,调度之后可通过Future.get()阻塞直至任务执行完毕,并且可以获取执行结果
    public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);
    //带延迟时间的调度,循环执行,固定频率
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
    //带延迟时间的调度,循环执行,固定延迟
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);
    
    ScheduledThreadPoolExecutor

    基于线程池的定时调度实现。可以用来在给定延时后执行异步任务或者周期性执行任务。

    ScheduledExecutorService4种调度机制的实现。

    2 Spring线程调度

    image-20200707232349760

    TaskExecutor

    Spring引入了TaskExecutor接口作为顶层接口。继承至J.U.C的Executor接口。

    TaskScheduler

    类似于J.U.C中的ScheduledExecutorService,是任务调度的接口。

    功能与ScheduledExecutorService差不多,多了一个可传Trigger对象的schedule()方法,可支持Corn表达式进行任务调度。

    ScheduledFuture<?> schedule(Runnable task, Trigger trigger);
    ScheduledFuture<?> schedule(Runnable task, Date startTime);
    ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period);
    ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period);
    ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Date startTime, long delay);
    ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay);
    
    AsyncTaskExecutor

    接口中提供submit()方法执行任务。

    ThreadPoolTaskExecutor

    Spring提供的线程池类。与Java提供的ThreadPoolExecutor线程池类类似,可以配置线程池属性及拒绝策略。

    处理流程与ThreadPoolExecutor一样。

    ThreadPoolTaskScheduler

    实现TaskScheduler接口,实现了其接口的调度机制。与Java中的ScheduledThreadPoolExecutor类类似。

    多了一个可传Trigger对象的schedule()方法,可支持Corn表达式进行任务调度。

    3 Spring异步操作

    使用@Async注解

    标注了@Async注解的方法,称之为异步方法。这些方法将在执行的时候,将会在独立的线程(线程池中获取)中被执行,调用者无需等待它的完成,即可继续其他的操作。

    @Configuration
    @EnableAsync
    public class AsyncConfig {
        
    }
    
    @Async
    public void downloadOrder() throws InterruptedException {
        System.out.println("执行开始时间为:" + LocalDateTime.now() + "线程为:" + Thread.currentThread().getName());
        Thread.sleep(10000);
        System.out.println("执行结束时间为:" + LocalDateTime.now() + "线程为:" + Thread.currentThread().getName());
    }
    

    @Async标注的方法,在同一个类中调用,无效

    4 Spring定时任务的几个实现

    4.1 基于@Scheduled注解
    @Configuration      //标记为配置类
    @EnableScheduling   //开启定时任务,可以放到启动类中
    public class OrderScheduler {
        //定时任务执行周期,采用Corn表达式
        @Scheduled(cron = "0/5 * * * * ?")
        public void downloadOrder() {
            System.out.println("执行开始时间为:" + LocalDateTime.now() + "线程为:" + Thread.currentThread().getName());
            Thread.sleep(10000);
            System.out.println("执行结束时间为:" + LocalDateTime.now() + "线程为:" + Thread.currentThread().getName());
        }
    }
    //并不是每5秒执行一次,上次执行的结果会影响到下次运行。这个相当于10s运行一次了。
    

    @Scheduled注解也可以直接指定时间间隔,如:@Scheduled(fixedRate=5000)

    实现本质是基于Java中的ScheduledExecutorService类的schedule方法。

    基于@Scheduled注解默认为单线程的。

    标注了@Scheduled的方法,执行时是单线程的,也就是说,上次运行的结果会影响到下一次的执行。

    image-20200706233720429

    4.2 基于SchedulingConfigurer接口
    @FunctionalInterface
    public interface SchedulingConfigurer {
    	void configureTasks(ScheduledTaskRegistrar taskRegistrar);
    }
    
    ScheduledTaskRegistrar类

    实现SchedulingConfigurer接口,实现configureTasks方法,方法传入ScheduledTaskRegistrar类对象。

    几种定时任务类型:

    • CronTask:根据Cron表达式执行定时任务
    • TriggerTask:按照Trigger触发器触发执行,可以动态改变定时任务的执行
    • FixedRateTask:固定速度执行
    • FixedDelayTask:固定延迟执行
    @EnableScheduling //启动类,设置定时任务开关
    @SpringBootApplication
    public class MatrixApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(MatrixApplication.class, args);
        }
    
    }
    
    @Component
    public class TestScheduler implements SchedulingConfigurer {
    
        @Override
        public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
            scheduledTaskRegistrar.addTriggerTask(() -> System.out.println("执行时间:" + LocalDateTime.now()), new CronTrigger("0/2 * * * * ?"));
        }
    }
    

    SchedulingConfigurer配置类比@Scheduled注解多了一个TriggerTask,更加灵活,其他的差不多。

    4.3 基于注解设定多线程定时任务

    @Scheduled注解默认为单线程的,我们可以使用@Async注解启用多线程。

    @Configuration      //标记为配置类
    @EnableAsync        //开启多线程
    @EnableScheduling   //开启定时任务,可以放到启动类中
    public class OrderScheduler {
        //定时任务执行周期,采用Corn表达式
        @Async
        @Scheduled(cron = "0/5 * * * * ?")
        public void downloadOrder() {
            System.out.println("执行开始时间为:" + LocalDateTime.now() + "线程为:" + Thread.currentThread().getName());
            Thread.sleep(10000);
            System.out.println("执行结束时间为:" + LocalDateTime.now() + "线程为:" + Thread.currentThread().getName());
        }
    }
    

    image-20200706233756167

    同一个任务的每一次定时任务执行,都会开辟一个新的线程,不会影响到下一次任务的执行。

    4.4 ThreadPoolTaskScheduler

    利用线程池实现任务调度。

    @Resource
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;
    
    @GetMapping("/start")
    public void test() {
        threadPoolTaskScheduler.schedule(() -> System.out.println("第" + i + "执行时间:" + LocalDateTime.now()), new CronTrigger("0/2 * * * * ?"));
    }
    
    4.5 整合Quartz

    定时任务内部的方法可以结合@Async注解使用达到多线程的目的

    参考文章
    1. https://www.cnblogs.com/frankyou/p/10135212.html
    2. https://blog.csdn.net/weixin_43168010/article/details/97613895
  • 相关阅读:
    C#中跨线程访问控件问题解决方案
    asp.net网站中配置文件的加密
    C#中XML使用总结
    连接加密Access数据库的字符串设置方法
    asp.net中常用的26个优化性能的方法
    C#中Math的使用总结
    关于ASP.NET页面打印技术的总结
    域登录获取用户名字的控制
    Web界面设计基本原则
    域登录获得用户名称
  • 原文地址:https://www.cnblogs.com/lyldelove/p/13270351.html
Copyright © 2020-2023  润新知