面试题:说一下Java开启异步线程的几种方法?

文章目录

  • 整体描述
  • 实现方法
  • 一、注解@Async
    • 1. 添加注解
    • 2. 创建异步方法Service和实现类
    • 3. 调用异步方法
  • 二、AsyncManager
    • 1. 创建AsyncManager类
    • 2. 创建一个耗时的操作类
    • 3. 执行异步操作
  • 三、线程池
    • 1. 创建线程池
    • 2. 创建一个耗时的操作类
    • 3. 执行线程池
  • 总结


整体描述

在java中异步线程很重要,比如在业务流处理时,需要通知硬件设备,发短信通知用户,或者需要上传一些图片资源到其他服务器这种耗时的操作,在主线程里处理会阻塞整理流程,而且我们也不需要等待处理结果之后再进行下一步操作,这时候就可以使用异步线程进行处理,这样主线程不会因为这些耗时的操作而阻塞,保证主线程的流程可以正常进行。
最近在项目中使用了很多线程的操作,在这做个记录。


实现方法

线程的操作,是java中最重要的部分之一,实现线程操作也有很多种方法,这里仅介绍几种常用的。在springboot框架中,可以使用注解简单实现线程的操作,还有AsyncManager的方式,如果需要复杂的线程操作,可以使用线程池实现。下面根据具体方法进行介绍。

一、注解@Async

springboot框架的注解,使用时也有一些限制,这个在网上也有很多介绍,@Async注解不能在类本身直接调用,在springboot框架中,可以使用单独的Service实现异步方法,然后在其他的类中调用该Service中的异步方法即可,具体如下:

1. 添加注解

在springboot的config中添加 @EnableAsync注解,开启异步线程功能

package com.thcb.boot.config;import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;/*** MyConfig** @author thcb*/@Configuration
@EnableAsync
public class MyConfig {// 自己配置的Config
}

2. 创建异步方法Service和实现类

使用service实现耗时的方法

Service类:

package com.thcb.execute.service;import org.springframework.scheduling.annotation.Async;/*** IExecuteService** @author thcb*/
public interface IExecuteService {/*** 一些耗时的操作,使用单独线程处理* 这里就简单写了一个sleep5秒的操作*/@Asyncpublic void sleepingTest();
}

Service实现类:

package com.thcb.execute.service.impl;import com.thcb.execute.service.IExecuteService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;/*** ExecuteService业务层处理** @author thcb*/
@Service
public class ExecuteServiceImpl implements IExecuteService {private static final Logger log = LoggerFactory.getLogger(ExecuteServiceImpl.class);@Overridepublic void sleepingTest() {log.info("SleepingTest start");try {Thread.sleep(5000);} catch (Exception e) {log.error("SleepingTest:" + e.toString());}log.info("SleepingTest end");}
}

3. 调用异步方法

这里根据Springboot的框架,在controller层调用,并使用log查看是否时异步结果。

controller:

package com.thcb.boot.controller;import com.thcb.execute.service.IExecuteService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** TestController** @author thcb*/
@RestController
public class TestController {private static final Logger log = LoggerFactory.getLogger(TestController.class);@Autowiredprivate IExecuteService executeService;@RequestMapping("/test")public String test() {return "spring boot";}@RequestMapping("/executeTask")public String executeTask() {log.info("executeTask Start!");executeService.sleepingTest();log.info("executeTask End!");return "executeTask";}
}

在log查看结果:

图片

接口直接返回了executeTask,并log出executeTask End!在5秒之后,log打出SleepingTest end,说明使用了异步线程处理了executeService.sleepingTest的方法。

二、AsyncManager

使用AsyncManager方法,也是SpringBoot框架中带的任务管理器,可以实现异步线程。

1. 创建AsyncManager类

使用AsyncManager首先需要创建一个AsyncManager类,这个在springboot框架中应该也是有的:

/*** 异步任务管理器** @author thcb*/
public class AsyncManager {/*** 操作延迟10毫秒*/private final int OPERATE_DELAY_TIME = 10;/*** 异步操作任务调度线程池*/private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");/*** 单例模式*/private AsyncManager() {}private static AsyncManager me = new AsyncManager();public static AsyncManager me() {return me;}/*** 执行任务** @param task 任务*/public void execute(TimerTask task) {executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);}/*** 停止任务线程池*/public void shutdown() {Threads.shutdownAndAwaitTermination(executor);}
}

2. 创建一个耗时的操作类

这里同样需要创建一个耗时的操作,也是用sleep模拟:

public TimerTask sleepingTest() {return new TimerTask() {@Overridepublic void run() {// 耗时操作try {Thread.sleep(5000);} catch (Exception e) {log.error("SleepingTest:" + e.toString());}}};
}

3. 执行异步操作

使用AsyncManager执行异步操作也比较简单,直接调用即可:

// 异步线程池
AsyncManager.me().execute(sleepingTest());

三、线程池

使用线程池可以设定更多的参数,线程池在网上也有很多详细的介绍,在这我只介绍一种,带拒绝策略的线程池。

1. 创建线程池

创建带有拒绝策略的线程池,并设定核心线程数,最大线程数,队列数和超出核心线程数量的线程存活时间:

/*** 线程池信息: 核心线程数量5,最大数量10,队列大小20,超出核心线程数量的线程存活时间:30秒, 指定拒绝策略的*/
private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 30, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(20), new RejectedExecutionHandler() {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {log.error("有任务被拒绝执行了");}
});

2. 创建一个耗时的操作类

由于线程池需要传入一个Runnable,所以此类继承Runnable,还是用sleep模拟耗时操作。

/*** 耗时操作*/
static class MyTask implements Runnable {private int taskNum;public MyTask(int num) {this.taskNum = num;}@Overridepublic void run() {System.out.println("正在执行task " + taskNum);try {Thread.sleep(4000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("task " + taskNum + "执行完毕");}
}

3. 执行线程池

开启线程池,这里通过一个for循环模拟一下,可以看一下log输出,有兴趣的可以修改一下for循环和sleep的数值,看看线程池具体的操作和拒绝流程。

for (int i = 0; i < 20; i++) {MyTask myTask = new MyTask(i);threadPoolExecutor.execute(myTask);System.out.println("线程池中线程数目:" + threadPoolExecutor.getPoolSize() + ",队列中等待执行的任务数目:" +threadPoolExecutor.getQueue().size() + ",已执行完别的任务数目:" + threadPoolExecutor.getCompletedTaskCount());
}
threadPoolExecutor.shutdown();

总结

在此写一些线程操作需要注意的地方:

  • 线程数量和cpu有关,使用线程时一定要注意线程的释放,否则会导致cpu线程数量耗尽;
  • 使用注解完成的线程操作,不可以在自己的类中实现调用,因为注解最后也是通过代理的方式完成异步线程的,最好时在单独的一个service中写;
  • 线程池最好单独写,使用static和final修饰,保证所有使用该线程池的地方使用的是一个线程池,而不能每次都new一个线程池出来,每次都new一个就没有意义了。

以上就是三种线程池的操作,写的不算很详细,有兴趣的同学可以自己在深入研究一下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/600730.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用STM32的定时器和PWM实现LCD1602的背光控制

使用STM32的定时器和PWM功能来控制LCD1602的背光是一种常见的方法&#xff0c;它可以实现背光的亮度调节和闪烁效果。在本文中&#xff0c;我们将讨论如何利用STM32的定时器和PWM来实现LCD1602的背光控制&#xff0c;并提供相应的代码示例。 1. 硬件连接和初始化 首先&#x…

大语言模型LLM微调技术:Prompt Tuning

1 预训练语言模型概述 1.1 预训练语言模型的发展历程 截止23年3月底&#xff0c;语言模型发展走过了三个阶段&#xff1a; 第一阶段 &#xff1a;设计一系列的自监督训练目标&#xff08;MLM、NSP等&#xff09;&#xff0c;设计新颖的模型架构&#xff08;Transformer&#…

二蛋赠书十三期:《一书读懂物联网》

前言 大家好&#xff01;我是二蛋&#xff0c;一个热爱技术、乐于分享的工程师。在过去的几年里&#xff0c;我一直通过各种渠道与大家分享技术知识和经验。我深知&#xff0c;每一位技术人员都对自己的技能提升和职业发展有着热切的期待。因此&#xff0c;我非常感激大家一直…

标书制作的几个关键部分

在当今社会&#xff0c;无论是企业还是个人&#xff0c;参与招投标都是获取资源、项目或服务的重要途径。而一份优秀的标书&#xff0c;无疑是成功的关键。那么&#xff0c;标书究竟包含哪些部分呢&#xff1f; 1.封面与目录 封面是标书的脸面&#xff0c;它直接决定了评审专…

k8s的声明式资源管理(yaml文件)

1、声明式管理的特点 &#xff08;1&#xff09;适合对资源的修改操作 &#xff08;2&#xff09;声明式管理依赖于yaml文件&#xff0c;所有的内容都在yaml文件当中 &#xff08;3&#xff09;编辑好的yaml文件&#xff0c;还是要依靠陈述式的命令发布到k8s集群当中 kubect…

js 颜色转换,RGB颜色转换为16进制,16进制颜色转为RGB格式

颜色转换&#xff0c;RGB颜色转换为16进制,16进制颜色转为RGB格式&#xff0c;可以自己设置透明度。 //十六进制颜色值的正则表达式 var reg /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/; /*RGB颜色转换为16进制*/ String.prototype.colorHex function () {var that this;if (/^…

Python逻辑运算符详解

在Python中&#xff0c;逻辑运算符用于比较两个或多个条件&#xff0c;并根据条件的结果返回一个布尔值&#xff08;True或False&#xff09;。 Python中的逻辑运算符有三种&#xff1a;and、or和not。 and运算符 and运算符用于判断两个条件是否同时为True。只有当两个条件都…

软件验收测试计划、验收测试报告案例模板参考

1. 概述 1.1. 编写目的 1.2. 测试背景 1.3. 测试依据 1.4. 测试对象 1.5. 测试资源 2. 测试方式与环境 2.1. 测试方式 2.2. 测试环境 3. 测试结果 3.1. 功能适合性和准确性 3.1.1. 总体统计 3.1.2. 详细结果 3.2. 安全性 3.3. 可靠性和性能 4. 总体分析 5. 测试…

创建VScode的C/C++编译环境

创建VScode的C/C编译环境 安装软件json文件 安装软件 可以选择安装Dev-Cpp、BlockCode或者MinGW。这里选择了Dev-Cpp。 json文件 launch.json { "version": "0.2.0", "configurations": [ { "name": "&#xff08;gdb) L…

python解决如何判断平衡二叉树问题

对于二叉树中的特殊二叉树&#xff0c;平衡二叉树&#xff0c;要想解决判断二叉树是否是平衡二叉树问题&#xff0c;首先需要知道什么事平衡二叉树。 平衡二叉树的概念是指给定一颗二叉树&#xff0c;这颗二叉树以及二叉树的所有子二叉树的左右子树的高度之差不大于1&#xff…

redis的搭建及应用(六)-redis应用LUA脚本

edis的lua脚本 Lua 是一种轻量小巧的脚本语言&#xff0c;用标准C语言编写并以源代码形式开放&#xff0c; 其设计目的是为了嵌入应用程序中&#xff0c;从而为应用程序提供灵活的扩展和定制功能。 Lua 是巴西里约热内卢天主教大学&#xff08;Pontifical Catholic University …

STM32 IAP学习

STM32三种烧录方式 ISP&#xff1a;In System Programming&#xff08;在系统编程&#xff09; 执行芯片厂商的BootLoader程序进入ISP模式&#xff0c;进入ISP模式后&#xff0c;用户可选择官方提供的烧录通信接口&#xff08;如&#xff1a;串口&#xff09;&#xff0c;并配…

C++ UTF-8与GBK字符的转换 —基于Windows (MultiByteToWideChar WideCharToMultiByte)

1、UTF-8 和 GBK 的区别 GBK&#xff1a;通常简称 GB &#xff08;“国标”汉语拼音首字母&#xff09;&#xff0c;GBK 包含全部中文字符。 UTF-8 &#xff1a;是一种国际化的编码方式&#xff0c;包含了世界上大部分的语种文字&#xff08;简体中文字、繁体中文字、英文、…

word2019保存后的图片变模糊了怎么办?Word 2019 默认保存后压缩变模糊的问题,解决方案

Word 2019 默认保存后压缩变模糊的问题&#xff0c;解决方案 1&#xff0c;新建word 文件&#xff0c;插入一张原始图片&#xff0c;1080*1920&#xff0c;如下图&#xff1a; 2&#xff0c;保存时&#xff0c;word 2019默认选项&#xff0c;导致word 保存后&#xff0c;图片…

CTFshow web入门web127-php特性30

开启环境: extract() 函数从数组中将变量导入到当前的符号表&#xff0c;使用数组键名作为变量名&#xff0c;使用数组键值作为变量值 举例就是?a2&#xff0c;就会变成$a2&#xff0c;这里ctf_show有个_需要构造&#xff0c;前面说过php中变量名只有数字字母下划线&#xff…

<HarmonyOS主题课>1~3课后习题汇总

&#xff1c;HarmonyOS第一课&#xff1e;1~10课后习题汇总 1使用DevEco Studio高效开发 单选题 用哪一种装饰器修饰的组件可作为页面入口组件&#xff1f;&#xff08;B&#xff09; A. ComponentB. EntryC. PreviewD. Builder ArkTS Stage模型支持API Version 9&#xf…

uniapp 日历组件

我们的需求是显示当前月和下个月的排班表 引入 uniapp 日历组件 uni-calendar 做法有两种&#xff0c;一种是直接去修改组件&#xff0c;还有就是文档中提供的 selected 方法 修改组件的就不写了 <uni-calendar :lunar"true" :selected"selected" :in…

【demoSURF】室内定位(图像匹配)基础代码实现,包含所有可以出现问题的解法

代码如下 import numpy as np import cv2 from matplotlib import pyplot as plt plt.rcParams[font.sans-serif] [SimHei] # 用来正常显示中文标签 plt.rcParams[axes.unicode_minus] False # 用来正常显示负号img1 cv2.imread("D:/data/North/0007.JPG",0) i…

【C语言】自己代码实现字符串相关的常用API

目录 1、实现get(char *)&#xff1b;从键盘获取字符串2、实现put&#xff08;char *&#xff09;&#xff1b;输出字符串3、实现strlen&#xff08;char *&#xff09;&#xff1b;求字符串长度4、实现strcpy&#xff08;char *&#xff0c;char *&#xff09;&#xff1b;拷贝…

C#,入门教程(11)——枚举(Enum)的基础知识和高级应用

上一篇&#xff1a; C#&#xff0c;入门教程(10)——常量、变量与命名规则的基础知识https://blog.csdn.net/beijinghorn/article/details/123913570 不会枚举&#xff0c;就不会编程&#xff01; 枚举 一个有组织的常量系列 比如&#xff1a;一个星期每一天的名字&#xf…