springboot 多线程的使用

预备知识业务使用多线程的原因

  • 目的是面对高并发的时候 , 提高运行速度
场景一:一个业务逻辑有很多次的循环,每次循环之间没有影响,比如验证1万条url路径是否存在 , 正常情况要循环1万次,逐个去验证每一条URL,这样效率会很低 , 假设验证一条需要1分钟 , 总共就需要1万分钟,有点恐怖 。这时可以用多线程,将1万条URL分成50等份,开50个线程,没个线程只需验证200条 , 这样所有的线程执行完是远小于1万分钟的 。
场景二:需要知道一个任务的执行进度,比如我们常看到的进度条 , 实现方式可以是在任务中加入一个整型属性变量(这样不同方法可以共享) , 任务执行一定程度就给变量值加1,另外开一个线程按时间间隔不断去访问这个变量,并反馈给用户 。总之使用多线程就是为了充分利用cpu的资源,提高程序执行效率,当你发现一个业务逻辑执行效率特别低,耗时特别长,就可以考虑使用多线程 。
问题:不过CPU执行哪个线程的时间和顺序是不确定的,即使设置了线程的优先级,因此使用多线程的风险也是比较大的 , 会出现很多预料不到的问题,一定要多熟悉概念,多构造不同的场景去测试才能够掌握!
项目中可以通过:
@Order()设置运行的优先级 , 数字越小,级别越高FutureTask介绍参考: (101条消息) FutureTask详解_索码理的博客-CSDN博客_futuretask
线程池为什么要使用阻塞队列阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程,使得线程进入wait 状态,释放 cpu 资源,当队列中有任务时才唤醒对应线程从队列中取出消息进行执行 。使得在线程不至于一直占用cpu资源 。(线程执行完任务后通过循环再次从任务队列中取出任务进行执行,代码片段如:while (task != null || (task = getTask()) != null) {}) 。
不用阻塞队列也是可以的 , 不过实现起来比较麻烦而已,有好用的为啥不用呢
Spring线程池介绍:Spring 通过任务执行器(TaskExecutor)来实现多线程和并发编程 , 使用 ThreadPoolTaskExecutor 实现一个基于线程池的TaskExecutor,还得需要使用 @EnableAsync 开启异步,并通过在需要的异步方法那里使用注解@Async声明是一个异步任务Spring 已经实现的异常线程池:
  • SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程 。
  • SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作 。只适用于不需要多线程的地方
  • ConcurrentTaskExecutor:Executor的适配类,不推荐使用 。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类
  • SimpleThreadPoolTaskExecutor:是Quartz的 SimpleThreadPool 的类 。线程池同时被quartz和非quartz使用 , 才需要使用此类
  • ThreadPoolTaskExecutor :最常使用,推荐 。其实质是对 java.util.concurrent.ThreadPoolExecutor 的包装
同步使用ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();FutureTask<List> commentCallable = new FutureTask<>(GrouponRulesCallable);Future<Map> submit = executor.submit(commentCallable);Map map = submit.get();异步的使用:[(101条消息) SpringBoot实现多线程_喜欢前端的后端MelodyJerry的博客-CSDN博客_springboot实现多线程](https://blog.csdn.net/weixin_43438052/article/details/116108860#:~:text=SpringBoot实现 多线程 1 SpringBoot通过 任务执行器TaskExecutor 来实现多线程和并发编程 。2 使用,%40EnableAsync 开启对 异步任务 的支持,并通过在实际执行的Bean中的方法使用 %40Async注解 来声明这是一个 异步任务。)
JUC 的线程池并发下使用同步线程具体代码参考:litemall示例代码
  • 声明
private final static ArrayBlockingQueue<Runnable> WORK_QUEUE= new ArrayBlockingQueue<>(9);// handler – 由于达到线程边界和队列容量而阻塞执行时使用的处理程序private final static RejectedExecutionHandler HANDLER=new ThreadPoolExecutor.CallerRunsPolicy();private static ThreadPoolExecutor executorService = new ThreadPoolExecutor(16, 16, 1000, TimeUnit.MILLISECONDS, WORK_QUEUE, HANDLER);
  • 使用,通过 get() 方法获取他的返回值
//商品属性Callable<List> listCallable=()->goodsAttributeService.getGoodsAttributeList(litemallGoods.getGoodsSn());//商品规格Callable<Object> specificationCallable =()->specificationService.getGoodsSpecification(litemallGoods.getGoodsSn());FutureTask<List> goodsAttributeListTask = new FutureTask<>(listCallable);FutureTask<Object> objectCallableTask = new FutureTask<>(specificationCallable);executorService.submit(goodsAttributeListTask);executorService.submit(objectCallableTask);

推荐阅读