CompletableFuture.allOf() and Executor.shutdown()

这是两块内容 CompletableFuture.allOf() allOf()会非阻塞等待所有的CompletableFuture都完成,无论这些task中是否有抛出异常,也就是说即便其中有某些抛出了异常,allOf()的CompletableFuture调用join()阻塞等待时也不会立即抛出异常,而是等所有需等待的都完成,才会抛出异常,这也是allOf的含义之一 Executor.shutdown() 当ThreadPool调用shutdown()时,调用后只是不再接收新的Task,已提交的task会继续执行无任何影响,并且对该方法的调用并不会阻塞等待创建的thread结束。但如果main thread结束,那应该都结束。 另外就是当使用ThreadPool来run CompletableFuture时,若每次都new ThreadPool,则InheritableThreadLocal会正常,若使用默认或预定义的ThreadPool,则InheritableThreadLocal会失效(因为其只会在Create Thread时传递,而ThreadPool中的Thread是share的,而非每次都Create New)

InheritableThreadLocal and ThreadPools

https://stackoverflow.com/questions/7296623/inheritablethreadlocal-and-thread-pools 当ThreadLocal,尤其是InheritableThreadLocal和ThreadPool一起使用时,可能出现问题, 因为ThreadPool会复用线程,而子线程保存的一直是首次被call时,父线程的TL快照,当被复用时,ITL中的值是不会变的。所以即便在父线程中更新了TL(父线程大多是新的线程),被复用的子线程中的ITL也不会变。这会带来很多潜在的问题。一个推荐的解决方式是使用TL然后自行封装向子线程的传递。这个在link上有 https://github.com/lWoHvYe/unicorn/commit/a839e5cbac88d0bffb7fffc7a1b61a0b9be82386 在ITL的注释中,已经很明确了,是create时 when a child thread is created, the child receives initial values for all inheritable thread-local variables for which the parent has values.

Gateway整合Springdoc,Springfox

简单记录一下,后续再看看有没有更好的方式 Springfox 定义Bean,没有该Bean将会出现,在通过Gateway可以访问swagger-ui,但调用时未携带router-path导致404(定义该Bean但配置中的不同,也可能出现这问题) Springdoc 这个需要在Gateway中进行配置,这种感觉有些麻烦了,不知道有没有别的好的方式,并且gateway也需要引入springdoc依赖 通过 gateway-url/webjars/swagger-ui/index.html访问,可通过definition选项切换

Virtual Threads/Project Loom/Fiber

Virtual Threads即协程,将在Java 21 GA,针对这两年一直追的内容,做一些总结。 在Java中与Virtual Threads 相关的还有Structured Concurrency 和 Scoped Values,前者简化了对VT多线程的管理,而后者可以视为VT下的ThreadLocal。下面是摘录的各种文档。 为了管理多线程,可以使用Structured Concurrency 针对需要线程内共享的需求,可使用ScopedValues 在Spring Boot 3.2之前,可以这样配置 Web Servers & Task Execution使用VT 从Spring Boot 3.2起,只需要一条配置即可,会应用到Servlet Web Servers, Task Execution, Task Scheduling, Blocking Execution with Spring WebFlux, Http-NIO of Reactor, RabbitMQ/Kafka listener

Java反射 与 Lambda 函数映射

一、用「 Lambda 生成函数映射」代替「高频的反射操作」 我们来看一段最简单的反射执行代码: 上面的反射执行代码可以被改写成这样: fastjson2 中的具体实现的要复杂一点,但本质上跟上面一样,其本质也是生成了一个 function。 我们使用反射获取到的 Method 和 Lambda 函数分别执行 10000 次来看下处理速度差异: 处理速度相差居然达到 25 倍,使用 Java8 Lambda 为什么能提升这多呢? 答案就是:Lambda 利用 LambdaMetafactory 生成了函数映射代替反射。 下面我们详细分析下 Java反射 与 Lambda 函数映射 的底层区别。 1、反射执行的底层原理 注:以下只是想表达出反射调用本身的繁杂性,大可不必深究这些代码细节 如果要一句话解释的话,那就是通过元数据找到对应的方法,检查系统状态和反射参数没问题后就执行那个方法。 也就是说,正常执行一个方法的路径: java代码 => class字节码 => JVM解析字节码执行类加载过程 => 类加载获得元数据,JVM执行到对应的方法时根据元数据找到对应的方法开始执行 而反射执行的路径: java代码 => class字节码 => JVM解析字节码执行类加载过程 => JVM执行到反射部分的代码 => 通过反射系统拿到对应方法的元数据 => 检查系统状态和参数可以执行这个方法 => […]

详解Java lambda表达式

转自 Y组合子 函数式接口 Stream的实现原理 函数式编程(上) 函数式编程(下) Java 8引入了不少新特性,Function Interface, Stream, lambda,配合着范型,在看源码时会比较吃力,这篇文章前面大部分很基础,在上面的link中都有,后半部分结合源码和字节码稍有深度。比较推荐上面美团的函数式编程的讲解,虽然不太容易懂 本文的脉络 Lambda介绍 何为lambda 咱们首先来说说 Lambda 这个名字,Lambda 并不是一个什么的缩写,它是希腊第十一个字母 λ 的读音,同时它也是微积分函数中的一个概念,所表达的意思是一个函数入参和出参定义,在编程语言中其实是借用了数学中的 λ,并且多了一点含义,在编程语言中功能代表它具体功能的叫法是匿名函数(Anonymous Function),根据百科的解释: 匿名函数(英语:Anonymous Function)在计算机编程中是指一类无需定义标识符(函数名)的函数或子程序。 接着再来说说Lambda 的历史,虽然它在 JDK8 发布之后才正式出现,但是在编程语言界,它是一个具有悠久历史的东西,最早在 1958 年在Lisp 语言中首先采用,而且虽然Java脱胎于C++,但是C++在2011年已经发布了Lambda 了,但是 JDK8 的 LTS 在2014年才发布,所以 Java 被人叫做老土不是没有原因的,现代编程语言则是全部一出生就自带 Lambda 支持,所以Lambda 其实是越来越火的一个节奏~ Lambda 在编程语言中往往是一个匿名函数,也就是说Lambda 是一个抽象概念,而编程语言提供了配套支持,比如在 Java 中其实为Lambda 进行配套的就是函数式接口,通过函数式接口生成匿名类和方法进行Lambda 式的处理。 那么,既然是这一套规则我们明白了,那么Lambda 所提供的好处在Java中就是函数式接口所提供的能力了,函数式接口往往则是提供了一些通用能力,这些函数式接口在JDK中也有一套完整的实践,那就是 Stream。 不同语言中的Lambda Python 例子: C++ […]

AspectJ与LoadTimeWeaving动态代理

近期在看Spring Core的Doc,主要是IOC和AOP,其中有些已经了解了,有些是初次遇到,就比如这次的LTW 转自 前提介绍 当我们聊到Spring框架的项目实际开发中,用的强大的功能之一就是(面向切面编程)的这门AOP技术。如果使用得当,它的最大的作用就是侵入性比较少并且简化我们的工作任务(节省大量的重复性编码),最为重要的一点是,它可以让我们在不改变原有代码的情况下,织入我们的逻辑,尤其是在我们没有源代码的时候,而且当我们恢复之前的逻辑的时候,只需要去掉代理就可以了。 AOP的动态代理 Spring AOP的常规的实现方式为cglib和jdk动态代理。两者均可实现,只是性能上略有差异,此处不再详述。 当然,也是有变通的方案解决,比如将bean当做属性注入到自身,然后所有方法调用都通过这个属性来调用。或者通过AopContext.currentProxy的方式去获取代理对象。但是这些解决方案,在开发过程中开发者很容易因为疏忽导致出现问题。 所以,如果需要一种更加强大和易用的aop实现方案,那就是字节码编织技术aspectj。通过修改字节码,可以实现对所有方法进行切面,包括(final、private、static类型的方法),功能强大。并且spring支持aspectj方式的aop。 AOP技术类型 在介绍强大的Aspectj的技术之前,我们先进行对AOP技术实现基础进行分类,通过为目标类织入切面的方式,实现对目标类功能的增强。按切面被织如到目标类中的时间划分 这里我们介绍是Spring整合AspectJ的LTW机制。属于动态加载织入。 LTW可以解决的问题 LTW的原理 类加载期通过字节码编辑技术将切面织入目标类,这种方式叫做LTW(Load Time Weaving)。 使用JDK5 新增的 java.lang.instrument包,在类加载时对字节码进行转换,从而实现 AOP功能。 JDK的代理功能让代理器访问到JVM的底层组件,借此向JVM注册类文件转换器,在类加载时对类文件的字节码进行转换ClassFileTransformer接口。具体方向可以研究一下java agent技术即可。 Spring中实现LTW maven依赖和插件 spring-AOP 和 aspectJ spring的maven配置 springboot的配置 其中都会有maven aspectjWeaver包 spring全注解方式 springboot全注解方式 切面类 对象切点类 aop.xml配置 放到/src/main/resources/META-INF目录下 说明 @EnableAspectJAutoProxy 也会启动运行时代理植入方式。 启动 VM 参数 可以采用Maven方式进行打包进入执行 LTW 官方文档 EOF https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-aj-ltw-spring Tomcat, JBoss/WildFly, IBM WebSphere Application […]

数据库缓存的一致性

来源 | OSCHINA 社区 作者 | 腾讯云开发者社区 原文链接:https://my.oschina.net/qcloudcommunity/blog/5596831 导语 缓存合理使用确提升了系统的吞吐量和稳定性,然而这是有代价的。这个代价便是缓存和数据库的一致性带来了挑战,本文将针对最常见的 cache-aside 策略下如何维护缓存一致性彻底讲透。 但是客观上,我们的业务规模很可能要求着更高的 QPS,有些业务的规模本身就非常大,也有些业务会遇到一些流量高峰,比如电商会遇到大促的情况。 而这时候大部分的流量实际上都是读请求,而且大部分数据也是没有那么多变化的,如热门商品信息、微博的内容等常见数据就是如此。此时,缓存就是我们应对此类场景的利器。 关于一致性的文章网上有很多,这篇整体条理比较清晰,从基本解决方案(四种),到多步操作中某一步失败可能导致的一系列问题及解决。如同结尾讲的那样,每一种都有其使用场景,应结合具体场景进行选择。 缓存的意义 所谓缓存,实际上就是用空间换时间,准确地说是用更高速的空间来换时间,从而整体上提升读的性能。 何为更高速的空间呢? 更快的存储介质。通常情况下,如果说数据库的速度慢,就得用更快的存储组件去替代它,目前最常见的就是 Redis(内存存储)。Redis 单实例的读 QPS 可以高达 10w/s,90% 的场景下只需要正确使用 Redis 就能应对。 就近使用本地内存。就像 CPU 也有高速缓存一样,缓存也可以分为一级缓存、二级缓存。即便 Redis 本身性能已经足够高了,但访问一次 Redis 毕竟也需要一次网络 IO,而使用本地内存无疑有更快的速度。不过单机的内存是十分有限的,所以这种一级缓存只能存储非常少量的数据,通常是最热点的那些 key 对应的数据。这就相当于额外消耗宝贵的服务内存去换取高速的读取性能。 引入缓存后的一致性挑战 用空间换时间,意味着数据同时存在于多个空间。最常见的场景就是数据同时存在于 Redis 与 MySQL 上(为了问题的普适性,后面举例中若没有特别说明,缓存均指 Redis 缓存)。 实际上,最权威最全的数据还是在 MySQL 里的。而万一 Redis 数据没有得到及时的更新(例如数据库更新了没更新到 Redis),就出现了数据不一致。 大部分情况下,只要使用了缓存,就必然会有不一致的情况出现,只是说这个不一致的时间窗口是否能做到足够的小。有些不合理的设计可能会导致数据持续不一致,这是我们需要改善设计去避免的。 这里的一致性实际上对于本地缓存也是同理的,例如数据库更新后没有及时更新本地缓存,也是有一致性问题的,下文统一以 Redis […]

Spring 资源访问通配符和 classpath 和 classpath*区别

配置前缀 classpath: 从类路径中加载资源, classpath*:所有包含指定包名的路径 file: 使用URLResource从文件系统目录中装载资源, 请使用绝对路径 http:// 使用URLResource从web服务器中装载资源 ftp://使用URLResource从ftp服务器装载资源 无前缀 根据ApplicationContext的具体实现类采用对应类型的resource ,一般指定的是 classpath 下的路径 ( web项目中是 ServletContext 下的路径,基本一样 ) 匹配符 ?: 匹配文件名中的一个字符 *: 匹配文件名中的任意个字符 **:匹配多层路径 classpath:app-Beans.xml​说明:无通配符,必须完全匹配​classpath:App?-Beans.xml​说明:匹配一个字符,例如 App1-Beans.xml 、 App2-Beans.xml​classpath:user/*/Base-Beans.xml​说明:匹配零个或多个字符串(只针对名称,不匹配目录分隔符等),例如:user/a/Base-Beans.xml 、 user/b/Base-Beans.xml ,但是不匹配 user/Base-Beans.xml​classpath:user/**/Base-Beans.xml​说明:匹配路径中的零个或多个目录,例如:user/a/ab/abc/Base-Beans.xml,同时也能匹配 user/Base-Beans.xml​classpath:**/*-Beans.xml​说明:表示在所有的类路径中查找和加载文件名以“-Beans.xml”结尾的配置文件,但重复的文件名只加载其中一个,视加载顺序决定​classpath*:user/**/*-Beans.xml​classpath*:**/*-Beans.xml​说明:“classpath*:”表示加载多个资源文件( 依赖的jar包也会寻找 ),即使重名也会被加载 使用位置及示例 在 Spring 中任何需要加载资源的地方都可以使用 示例 : // 1ApplicationContext context = new ClassPathXmlApplicationContext(“classpath:spring-config.xml”);// 2Resource resource = context.getResource(“file:E:/Java_document/JavaWebTest/study-04/src/main/resources/hello.txt”);// 3Resource resource […]

lWoHvYe 无悔,专一