Java线程池ExecutorService和Executors应用(Spring Boot微服务)

2023-09-14 20:50:26

记录:476

场景:在Spring Boot微服务中使用ExecutorService管理Java线程池。使用Executors创建线程池。使用Runnable接口实现类提交线程任务到线程池执行。

版本:JDK 1.8,Spring Boot 2.6.3。

1.线程和线程池基础

JDK自带线程和线程池包位置:java.util.concurrent.*,以及java.lang.Runnable和java.lang.Thread在java.lang.*中。

1.1线程接口Runnable

接口全称:java.lang.Runnable。

接口注释:The Runnable interface should be implemented by any class whose instances are intended to be executed by a thread.

说明:提交给线程执行的类需实现Runnable接口。该接口只有一个抽象方法public abstract void run()。具体业务逻辑如需被线程调用的话,必须在此run方法内调用业务逻辑。

1.2线程Thread

类全称:java.lang.Thread。

类注释:A thread is a thread of execution in a program. The Java Virtual Machine allows an application to have multiple threads of execution running concurrently.

说明:线程java.lang.Thread实现了java.lang.Runnable接口。在java.lang.Thread中维护了JVM中对线程的属性和方法的操作。方法包括线程创建、初始化、启动、运行、停止等。属性包括

线程名称、优先级、守护进程标志位、线程组、线程本地变量等。

接收Runnable线程任务方式:一般在创建Thread线程对象时,在有参构造函数的输入参数中传入自定义线程任务(实现Runnable接口的对象)。比如:public Thread(Runnable target)。

1.3线程池

(1)接口

java.util.concurrent.Executor,线程池顶层接口,只有一个抽象方法void execute(Runnable command)。此方法执行提交给线程池已实现Runnable接口的线程任务。

java.util.concurrent.ExecutorService,线程池接口,实现java.util.concurrent.Executor接口。此接口中主要提供线程池提交任务的submit方法、和invokeAll方法等方法。

(2)抽象类

java.util.concurrent.AbstractExecutorService,线程池默认实现方式,在AbstractExecutorService中提供默认的实现ExecutorService接口的方式。

(3)实现类

java.util.concurrent.ThreadPoolExecutor,线程池实现类。

主要是对线程池的创建、运行、维护等方面管理。

属性包括运行状态、线程池大小、线程池任务数量、线程池工厂类ThreadFactory、线程池工作线程、线程池同步线程锁、线程池任务队列BlockingQueue<Runnable> workQueue等。

方法包括一序列有参构造函数创建线程池、线程池执行任务方法void execute(Runnable command),以及获取线程池相关属性的get方法和设置线程池相关属性的set方法。

1.4线程池创建工具类Executors

全称:java.util.concurrent.Executors。

说明:在Executors中包括一序列创建线程池的静态方法,此类构造方法是private类型,因此不可被实例化。

类方法包括如下,可按需选择。

public static ExecutorService newFixedThreadPool(int nThreads);
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory);
public static ExecutorService newWorkStealingPool();
public static ExecutorService newWorkStealingPool(int parallelism)
public static ExecutorService newSingleThreadExecutor();
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) ;
public static ExecutorService newCachedThreadPool();
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory);
public static ScheduledExecutorService newSingleThreadScheduledExecutor();
public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory);
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize);
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory);

1.5其它

(1)线程专用的一些类

java.lang.Runnable

java.util.concurrent.Callable

java.util.concurrent.Future

java.util.concurrent.FutureTask

(2)线程工程类

线程池工厂(接口)

java.util.concurrent.ThreadFactory

线程池工厂(实现类)

java.util.concurrent.Executors.DefaultThreadFactory

java.util.concurrent.Executors.PrivilegedThreadFactory

(3)调度类线程体现

接口

java.util.concurrent.Executor

java.util.concurrent.ExecutorService

java.util.concurrent.ScheduledExecutorService

实现类

java.util.concurrent.ThreadPoolExecutor

实现类

java.util.concurrent.ScheduledThreadPoolExecutor

(4)其它

java.lang.SecurityManager

java.util.concurrent.ThreadPoolExecutor.Worker

2.使用Spring Boot注解配置线程池ExecutorService

(1)说明

ThreadPoolExecutor,全称:java.util.concurrent.ExecutorService。

使用@Bean("executorServiceHz")注解把线程池注入到Spring IOC容器中。

(2)代码

@Configuration
public class ThreadPoolConfig {
  /**
   * 创建线程池
   */
  @Bean("executorServiceHz")
  public ExecutorService executorService() {
      ExecutorService executorService = Executors.newFixedThreadPool(8, new ThreadFactory() {
          @Override
          public Thread newThread(Runnable runnable) {
              return new Thread(runnable);
          }
      });
      return executorService;
  }
}

3.实现Runnable接口的线程任务类

(1)说明

提交给线程池任务,需实现Runnable接口。

Runnable接口的run方法里面就是线程具体执行的业务逻辑。

(2)代码

public class SportContestExecutor implements Runnable {
    private TaskDto taskDto;
    public SportContestExecutor(TaskDto taskDto) {
        this.taskDto = taskDto;
    }
    @Override
    public void run() {
        String eventName = this.taskDto.getEventName();
        String content = this.taskDto.getContent();
        System.out.println("【线程: " + Thread.currentThread().getName() + ",在直播: " + eventName + content + "】");
    }
}

4.把实现Runnable接口的线程任务类提交到线程池

(1)说明

Runnable接口的线程任务类需提交到线程池才能具体执行。

(2)代码

@Component("sportWorker02")
public class SportWorker02 {
  /**
   * 自动注入线程池
   */
  @Autowired
  private ExecutorService executorServiceHz;
  /**
   * 把线程任务提交到线程池
   */
  public void holdGame() {
  
      //1.准备数据
      List<TaskDto> groupStage = new ArrayList<>();
      for (int i = 0; i < 10; i++) {
          String no = "" + (i + 1);
          if (i < 9) {
              no = "0" + (i + 1);
          }
          TaskDto taskDto01 = TaskDto.builder().eventName("羽毛球球比赛").content("小组赛" + no).build();
          groupStage.add(taskDto01);
      }
      //2.线程任务提交到线程池
      for (TaskDto taskDto : groupStage) {
          executorServiceHz.execute(new SportContestExecutor(taskDto));
      }
  }
}

5.测试示例

(1)说明

直接在SpringBoot的启动类的main函数中测试。

在执行完成SpringApplication.run(Example212Application.class)后,SpringBoot的环境已经创建完成。

(2)代码

@SpringBootApplication
public class Example212Application {
    public static void main(String[] args) {
        SpringApplication.run(Example212Application.class);
        SportWorker02 sportWorker02 = SpringUtil.getBean("sportWorker02");
        sportWorker02.holdGame();
    }
}

(3)输出结果

【线程: Thread-5,在直播: 羽毛球球比赛小组赛02】
【线程: Thread-4,在直播: 羽毛球球比赛小组赛01】
【线程: Thread-6,在直播: 羽毛球球比赛小组赛03】
【线程: Thread-7,在直播: 羽毛球球比赛小组赛04】
【线程: Thread-8,在直播: 羽毛球球比赛小组赛05】
【线程: Thread-7,在直播: 羽毛球球比赛小组赛09】
【线程: Thread-5,在直播: 羽毛球球比赛小组赛10】
【线程: Thread-10,在直播: 羽毛球球比赛小组赛07】
【线程: Thread-9,在直播: 羽毛球球比赛小组赛06】
【线程: Thread-11,在直播: 羽毛球球比赛小组赛08】

6.辅助实体类

(1)说明

在实体类中使用注解@Data等来自lombok-1.18.24.jar。

(2)TaskDto

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class TaskDto implements Serializable {
    //赛事名称
    private String eventName;
    //活动内容
    private String content;
}

以上,感谢。

2023年9月14日

更多推荐

Learned-Based VO 梳理(USTC Paper Reading)

梗概主题:VOinDynamic思路简答介绍SLAM框架,引入VO问题直接从LearnedVO开始,介绍VO问题的相关研究,总结当前研究存在的问题讨论DynamicVO中的相关做法,总结问题,提出想法拓展VO和navigation的关系引入SLAM的全称是SimultanousLocationAndMapping,同步

【软件工程】软件工程之道—《人月神话》读后思考

目录写在前面1图书介绍2读后思考写在前面《人月神话》是一本由弗雷德里克·布鲁克斯(FrederickP.Brooks)所著的计算机科学经典著作。这本书首次出版于1973年,至今仍然被认为是计算机科学领域的重要参考书籍之一。它对我产生了深远的影响。通过作者弗雷德里克·布鲁克斯的深入剖析和丰富的实践经验,我对软件开发项目的

✔ ★ 算法基础笔记(Acwing)(三)—— 搜索与图论(17道题)【java版本】

搜索与图论1.DFS1.排列数字(3分钟)2.n-皇后问题2.BFS(队列)1.走迷宫二刷总结(队列存储一个节点pair<int,int>)三刷总结走过的点标记上距离(既可以记录距离,也可以判断是否走过)★★例题2.八数码二刷总结3.树与图的dfs1.树的重心二刷总结1.如何找根节点?用无向图遍历,则不需要根节点2.把

红与黑(bfs + dfs 解法)(算法图论基础入门)

红与黑问题文章目录红与黑问题前言问题描述bfs解法dfs解法前言献给阿尔吉侬的花束(入门级bfs查找+模版解读+错误示范在之前的博客当中,详细地介绍了这类题目的解法,今天为大家带来一道类似的题目练练手,后续还会更新更有挑战的题目以及更为详细的解析,喜欢的小伙伴可以点个关注啦!问题描述有一间长方形的房子,地上铺了红色、黑

8个很棒的Vue开发技巧

1.路由参数解耦通常在组件中使用路由参数,大多数人会做以下事情。exportdefault{methods:{getParamsId(){returnthis.$route.params.id}}}在组件中使用$route会导致与其相应路由的高度耦合,通过将其限制为某些URL来限制组件的灵活性。正确的做法是通过prop

实现 3D 倒计时器

构建单个倒计时器卡片实现思路从上述的总体效果图来看,单个倒计时器的卡片主要是分为头部为尾部两个部分,所以我们可以采用flex布局来实现整体的布局,并且利用flex布局实现文字内容的布局。具体实现步骤如下:编写HTML结构<divclass="flip_cardflip"><divclass="top">4</div><

openGauss学习笔记-69 openGauss 数据库管理-创建和管理普通表-更新表中数据

文章目录openGauss学习笔记-69openGauss数据库管理-创建和管理普通表-更新表中数据openGauss学习笔记-69openGauss数据库管理-创建和管理普通表-更新表中数据修改已经存储在数据库中数据的行为叫做更新。用户可以更新单独一行、所有行或者指定的部分行。还可以独立更新某个字段,而其他字段则不受

英国8月CPI意外降温,然而加息决定仍悬而未决

KlipC报道:据英国国家统计局公布最新数据显示,8月CPI同比上涨6.7%,低于上月数据,核心CPI增幅低于经济学家的预测。数据公布后,英镑走弱、英债收益率下跌,英镑应声下跌0.5%至5月以来的最弱水平,两年期英债债券收益率下跌至4.298%。KlipC的合伙人AndiD表示:“8月CPI涨幅微降主要原因是食品价格涨

西门子828d授权密钥破解经验分享 I7I54833762

操作数组的方法Array.prototype.toSorted(compareFn)//返回一个新数组,其中元素按升序排序,而不改变原始数组。Array.prototype.toReversed()//返回一个新数组,该数组的元素顺序被反转,但不改变原始数组。Array.prototype.toSpliced(star

【Qt】Unicode编码作用 ,以及在Qt中的理解

Unicode编码是一种字符编码标准,它为世界上几乎所有的字符都分配了一个唯一的数字标识符,以便在计算机系统中进行存储和处理。Unicode编码的作用有以下几点:统一字符表示:Unicode编码提供了一个统一的字符集,使得不同语言、不同文化背景的字符都能够被准确地表示和处理。它包括了世界上几乎所有的字符,包括字母、数字

【VS2019 Qt5 VTK9.2】界面编程问题&解决记录

一、Qt和VTK相关问题及解决1.Widget和Viewer的设置顺序imageViewer->SetupInteractor(renderWindow->GetInteractor());ui.qvtkWidget->setRenderWindow(imageViewer->GetRenderWindow());二者

热文推荐