有状态InheritableThreadLocal 配合 JDK8 ,异步方法调用
我们可以把一个类的作用域注解为
@Scope(scopeName = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
这样这个类就能在session中获取,可以把用户信息放到这个类中,需要的时候,直接@Autowire进来就好了.
但是这样有一个坑.在主线程中,如果使用JDK异步方法,或者自己new出新的线程中,没有办法注入.会提示一个异常
Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton;
这个异常非常误导人,在单线程下是不会提示的.提示了这个异常,原因是:session 是根据request获取的,那么Spring是用requestContextHolder实现的,原理还是InheritableThreadLocal ,按道理来说,在子线程也是可以获取到父线程的request.getSession,至于为什么在子线程中没有激活这个session作用域,导致注入失败,估计是Spring的设计问题.
所以参考RequestContextHolder,自己可以写一个记录结果以及错误信息的LocalResut
虽然我用了没有问题,但是所有书籍记录中,都不推荐在并发流或者多线程中使用有状态对象,虽然我这不涉及竞态条件,但是如果因为线程回收慢导致ThreadLocal数据没有回写到主线程就已经返回了,或者可见性问题,那么结果也会有误,而且不好debug
这里用一个ThreadLocal 记录结果集,或者错误信息,等调用完并发流以后,可以join之后,输出结果,注意线程可见性问题,这加上volatile 修饰.
public class LocalResult{
private static volatile ThreadLocal<Map<String, String>> mapMessage = new InheritableThreadLocal(); public LocalError() {
} public static void mapMessageCreate() {
mapMessage.remove();
mapMessage.set(new HashMap());
} public static ThreadLocal<Map<String, String>> getMapMessage() {
return mapMessage;
}
}
在这里根据订单的id数组,进行批量的异步结算,在结算方法中,如果有错误信息,依然会调用LocalResult,
线程池等于需要并发结算的数量+3 是一个容错,避免线程池的Thead不够,导致ThreadLocal数据乱套
for开启异步方法,之后马上关闭线程池
然后还要等待线程池所有线程已经回收以后才让方法返回:是因为线程执行完方法,不一定立即回收,当线程已经开启,马上调用线程池的shutdown方法,告诉线程执行完后,尽快回收.
然后循环判断线程池是否完全回收后,才返回因为,根据join()规则。假设线程A在执行的过程中,通过执行ThreadB.join()来等待线
程B终止;同时,假设线程B在终止之前修改了一些共享变量,线程A从ThreadB.join()返回后会
读这些共享变量。
.
public void doAsync(Integer [] ids) {
List<CompletableFuture<Boolean>> futures = new ArrayList<>(ids.length);
ExecutorService executor = Executors.newFixedThreadPool(ids.length+3);
for (Integer id : ids) {
Order order = orderService.findEntityById(id);
if (Order.Type.Failure.getCode().equals(orderInfo.getType())) {
LocalResult.getMapMessage().get().put(order.getOrderNo(), "单号:" + order.getOrderNo() + ",已作废,不能结算!");
continue;
}
futures.add(CompletableFuture.supplyAsync(() -> this.do(orderInfo),executor));
}
executor.shutdown();
futures.stream().map(CompletableFuture::join);
while (!executor.isTerminated()) {
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
break;
}
}
}
controller
@PostMapping(path = "/do",produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResultDataDto billing(@RequestParam("ids") Integer[] ids) {
LocalResult.mapMessageCreate();
service.doAsync(ids);
if (LocalResult.getMapMessage().get().size() > 0) {
StringBuilder stringBuilder = new StringBuilder();
for (Map.Entry<String, String> entry : LocalResult.getMapMessage().get().entrySet()) {
stringBuilder.append("单号:" + entry.getKey() + ":" + entry.getValue() + "</br>");
}
return ResultDataDto.addOperationSuccess("以下失败!:</br>" + stringBuilder.toString());
}
return ResultDataDto.addOperationSuccess("结算成功!");
}
有状态InheritableThreadLocal 配合 JDK8 ,异步方法调用的更多相关文章
- ICE的异步方法调用
转自:http://blog.sina.com.cn/s/blog_45497dfa0100nwbr.html http://www.cnblogs.com/mawanglin2008/article ...
- asyn4j -- java 异步方法调用框架
asyn4j 是一个java异步方法调用框架,基于消费者与生产者模式.包括了异步方法执行,异步回调执行,异步工作缓存模块.支持Spring. 让我们写异步方法不再写很多的相关多线程代码.用asyn4j ...
- JavaEE Tutorials (7) - 在会话bean中使用异步方法调用
7.1异步方法调用88 7.1.1创建异步业务方法88 7.1.2从企业bean客户端调用异步方法897.2async示例应用90 7.2.1async—war模块的架构91 7.2.2运行async ...
- Vue之状态管理(vuex)与接口调用
Vue之状态管理(vuex)与接口调用 一,介绍与需求 1.1,介绍 1,状态管理(vuex) Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态 ...
- springboot(整合多数据源demo,aop,定时任务,异步方法调用,以及获取properties中自定义的变量值)
有这么一个需求 每个部门,需要操作的数据库不同,A部门要将数据放test数据库,B 部门数据 要放在test1数据库 同一个项目 需要整合 多个数据源 上传个demo 方便自己以后回看!!!!!!!! ...
- Asynchronous_method_invocation 异步方法调用 让步 yielding
zh.wikipedia.org/wiki/同步 [同步不同事件发生 时间一致] 同步(英语:Synchronization),指在一个系统中所发生的事件(event),之间进行协调,在时间上出现一致 ...
- spring@Async注解实现异步方法调用
概述 如何实现异步方法调用,很多人首先会想到使用线程或者线程池技术,springboot中有一个很简单的方法可以实现异步方法调用,那就是在方法上使用@Async注解 例子 首先在Springboot启 ...
- C#多线程编程之:异步方法调用
异步方法 当一个线程调用方法后,直到方法执行完毕,线程才继续执行,这种方法被称为同步方法.然而,有些方法执行时间可能非常长,比如串口操作或访问网络,这样线程被阻塞,而无法响应用户的其他请求.这种情况通 ...
- odoo开发笔记 -- 借助模块queue_job实现异步方法调用
场景描述: 对比了几个定时调度的框架,发现各有优缺点: celery 很强,异步定时调度,异步周期调度,也有延时调度的功能,但是延时调度的案例比较少,遂暂时不使用. queue_job,一个odoo第 ...
随机推荐
- __init__class的简单使用/理解
# -*- coding: utf-8 -*- class Student(object): def __init__(self, name, score): #通过定义一个特殊的__init__方法 ...
- Zabbix-3.2.4实现微信(WeChat)告警
摘自abcdocker网站 原文地址:https://www.abcdocker.com/abcdocker/2472 Zabbix可以通过多种方式把告警信息发送到指定人,常用的有邮件,短信报警方式, ...
- nginx配置中root与alias的区别
nginx指定文件路径有两种方式root和alias,这两者的用法区别,使用方法总结了下,方便大家在应用过程中,快速响应.root与alias主要区别在于nginx如何解释location后面的uri ...
- 【转】SED多行模式空间
在前面看到的都是单行模式.每次sed处理一个行. 但是sed是允许一次处理多行的.这就是所谓的多行模式空间. 多行模式空间命令有(N.D.P),他们分别对应单行模式空间(n.d.p). 分别是他们的多 ...
- Tomcat8远程访问manager,host-manager被拒绝403
Tomcat部署在服务器之后在服务器本地访问manager和host-manager成功(即127.0.0.1:8080或者localhost:8080),但使用测试主机访问tomcat的manage ...
- java编码GBK的不可映射字符
编译java文件时,提示编码GBK的不可映射字符. 主要原因:windows 默认编码方式为GBK,用javac编译时,中文按照GBK解析,但是文件内容编码格式不是GBK. 解决:若编译单个文件指定编 ...
- FTP 安装配置
FTP 安装配置: 一.基础操作 yum install -y ftp yum install -y vsftpd service iptables stop chkconfig iptables o ...
- Linq to SQL 中实现模糊查询
list = list.Where(i => i.Name.Contains(empName)).ToList();
- java.lang.ClassNotFoundException: org.apache.jsp.WEB_002dINF.classes.views.index_jsp 问题解决方法
本人使用的是taglib作为模板页,然后碰到的这个问题,如果有类似的可以参考. <%@tag description="Overall Page template" page ...
- JavaScript基本语法 -- 条件语句 & 循环语句
条件语句 条件语句(Conditional statement)是JavaScript里面的基本结构之一,程序根据表达式的真假决定执行或者跳过某个分支,于是,条件语句有时候也可以称为"分支语 ...