Java 单线程池、多线程池、同步、异步的实践

您所在的位置:网站首页 多线程加锁和单线程加锁区别 Java 单线程池、多线程池、同步、异步的实践

Java 单线程池、多线程池、同步、异步的实践

2023-08-01 12:03| 来源: 网络整理| 查看: 265

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录 一、 需求:二、 解决方案三、 基本模块1.定义线程池配置,处理异步请求2.定义单线程池,处理顺序执行的异步请求3.定义线程,执行具体的工作4.定义接口API,往线程池中创建任务 四、测试结果总结

知识点: 线程池线程池: Executors.newSingleThreadExecutor() 多线程线程池: ThreadPoolTaskExecutor 一个可取消的异步线程: FutureTask

一、 需求:

假设:提供一个打印文件的服务:外围控制器负责发起打印请求,服务负责打印数据,打印数据有两种模式,关心打印顺序和不关心打印顺序两种,其中调用方只关心消息指令是否下发成功,不关心执行结果,存在并发 。

需求分析:

1.调用方只关心指令下发是否成功,不关心执行结果:可考虑异步,在接收到指令时立即返回“收到指令”,具体业务的执行过程所需要的时间; 2.并发:使用多线程的思想; 3.方法执行的顺序问题:可从同步执行、或独占线程的角度去考虑;

涉及内容:单线程线程池、多线程线程池、

//一个可取消的异步线程 FutureTask futureTask = new FutureTask();//使用 futureTask.get() 会等待线程的结果,将异步的方法变成同步 //单一线程池:只开启一个线程,所有任务排队顺序执行 ExecutorService executor = Executors.newSingleThreadExecutor(); //多线程线程池:同时开启多个线程,所有任务被分配不同线程异步执行 ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 二、 解决方案

Java 线程池、同步、异步实践

1.开启一个ApiController,接收外围系统触发打印信息; 2.开启一个线程池,异步处理,触发方法后立马给返回消息; 3.内部执行过程交由主线程去处理,且分别实现线程的顺序执行和异步执行;

三、 基本模块 1.定义线程池配置,处理异步请求 //AsyncConfig.class /** * 线程池的配置 * 放入线程池的线程并发执行 * *@author HeYQ *@date 2022/5/16 */ @Configuration //@EnableAsync public class AsyncConfig { //接收报文核心线程数 //@Value("${book.core.poolsize}")//后续可以定义在yml配置文件中 private int bookCorePoolSize = 20; //接收报文最大线程数 //@Value("${book.max.poolsize}") private int bookMaxPoolSize = 20; //接收报文队列容量 //@Value("${book.queue.capacity}") private int bookQueueCapacity = 20; //接收报文线程活跃时间(秒) //@Value("${book.keepAlive.seconds}") private int bookKeepAliveSeconds = 100; //接收报文默认线程名称 //@Value("${book.thread.name.prefix}") private String bookThreadNamePrefix = "print_recv"; /** * 接口的线程池 * * @return TaskExecutor taskExecutor接口 * @since JDK 1.8 */ @Bean(name = "threadPoolTaskExecutor") public ThreadPoolTaskExecutor threadPoolTaskExecutor() { //newFixedThreadPool ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();//多线程线程池,内部线程是异步执行的 // 设置核心线程数:一般线程池会至少保持这么多的线程数量 executor.setCorePoolSize(bookCorePoolSize); // 设置最大线程数:线程池最多有这么多的线程数量 executor.setMaxPoolSize(bookMaxPoolSize); // 设置队列容量 executor.setQueueCapacity(bookQueueCapacity); // 设置线程活跃时间(秒) executor.setKeepAliveSeconds(bookKeepAliveSeconds); // 设置默认线程名称 executor.setThreadNamePrefix(bookThreadNamePrefix); // 设置拒绝策略 // rejection-policy:当pool已经达到max size的时候,如何处理新任务 // CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行 //AbortPolicy:用于被拒绝任务的处理程序,它将抛出RejectedExecutionException //CallerRunsPolicy:用于被拒绝任务的处理程序,它直接在execute方法的调用线程中运行被拒绝的任务。 //DiscardOldestPolicy:用于被拒绝任务的处理程序,它放弃最旧的未处理请求,然后重试execute。 //DiscardPolicy:用于被拒绝任务的处理程序,默认情况下它将丢弃被拒绝的任务。 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 等待所有任务结束后再关闭线程池 executor.setWaitForTasksToCompleteOnShutdown(true); executor.initialize(); return executor; } } 2.定义单线程池,处理顺序执行的异步请求 //SyncConfig.class /** * 单线程池配置 * 放入线程池的线程顺序执行 * * @author HeYQ * @date 2022/5/16 */ @Configuration public class SyncConfig { @Bean(name = "singleThreadPool") public ExecutorService singleThreadPoolExecutor() { ExecutorService executor = Executors.newSingleThreadExecutor(); return executor; } } 3.定义线程,执行具体的工作 //PrintTask.java /** * 测试时使用的例子 * 调用该方法,创建一个线程,该现场可以传参,内部可以调用指定的方法 * * @author HeYQ * @date 2022/5/16 */ @Slf4j @Component //@Async public class PrintTask implements Callable { //操作类型 private String operationType; public PrintTask() { } //增加有参构造方法,使得创建线程时可以传值 public PrintTask(String operationType) { this.operationType = operationType; } @Override public synchronized Boolean call() throws Exception { String curId = PrimaryGenerator.getInstance().nextFormNo("");//自己的方法,获取一个主键ID,也可以自己写 int a = 0; while (a @Resource ThreadPoolTaskExecutor threadPoolTaskExecutor;//多线程池 @Resource ExecutorService singleThreadPoolExecutor;//单线程池 /** * 多线程打印:不要求方法执行的顺序 * * @author HeYQ */ @RequestMapping(value = "/asyncPrin", method = RequestMethod.POST) @ResponseBody public Result printHtml(@RequestBody LabelInfoDto tempInfo) throws Exception { try { Assert.isFalse(tempInfo.getTemplateType().equals(null), "打印失败,条码类型不能为空!"); FutureTask futureTask = new FutureTask(new PrintTask("printHtml")); threadPoolTaskExecutor.submit(futureTask); return Result.success("接收信息成功"); } catch (Exception ex) { log.error("打印时出现异常" + ex.getMessage()); return Result.success("接收信息时出现异常:" + ex.getMessage()); } } /** * 单线程打印:关心打印顺序 * * @author HeYQ */ @RequestMapping(value = "/syncPrin", method = RequestMethod.POST) @ResponseBody public Result printTest(@RequestBody LabelInfoDto tempInfo) throws Exception { try { Assert.isFalse(tempInfo.getTemplateType().equals(null), "打印失败,条码类型不能为空!"); FutureTask futureTask = new FutureTask(new PrintTask("printTest")); singleThreadPoolExecutor.submit(futureTask); //1.方法不会等待线程执行完,会立马返回 "接收信息成功" //log.error("拿到结果" + futureTask.get()); //2. futureTask.get() 会等待线程的结果,将异步的方法变成同步 return Result.success("接收信息成功"); } catch (Exception ex) { log.error("打印测试时出现异常" + ex.getMessage()); return Result.success("时出现异常:" + ex.getMessage()); } } } 四、测试结果

1.异步打印:不需要关心打印顺序,会存在交叉打印的情况 调用: 在这里插入图片描述

结果:调用了两次,两个线程穿插执行 在这里插入图片描述

2.同步打印:接口在调用时就得到返回值,内部方法交由线程池顺序执行,打印连续,不会存在穿插情况 在这里插入图片描述 结果:调用了两次,第二次会在第一次完全打印完之后才会执行 在这里插入图片描述

总结

以上就是今天要讲的内容,本文仅仅简单介绍了使用线程池实现了异步调用,并通过单线程线程池和多线程线程池的使用,实现了对线程内方法顺序的控制。 补充: FutureTask futureTask = new FutureTask(); 的使用。

//1.singleThreadPoolExecutor 为线程池 //2.创建PrintTask线程,定义为可取消的任务 FutureTask futureTask = new FutureTask(new PrintTask("printTest")); //3.将线程交由线程池托管 singleThreadPoolExecutor.submit(futureTask); //4.方法不会等待线程执行完,会立马返回 "接收信息成功" //log.error("拿到结果" + futureTask.get()); //5. 如果使用 futureTask.get() 会等待线程的结果,将异步的方法变成同步 return Result.success("接收信息成功");

可参看该片文章:彻底理解Java的Future模式



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3