当前位置: 首页 > news >正文

Java并行流parallelStream()下InheritableThreadLocal引起的问题

Java并行流parallelStream()下InheritableThreadLocal引起的问题

引起问题的代码。

List orgs00 = Arrays.asList(new Org("aaa"),new Org("bbb"),new Org("aa0"));List orgs = orgs00.parallelStream()  .map(org -> {// 模拟从数据库中获取 Org 对象的操作// 这里可以是实际的数据库查询操作ConditionImpl cmd = new ConditionImpl();cmd.op("orgName", Op.eq, org.getOrgName());
//	                                    cmd.orderBy("name", OrderType.ASC);System.out.println("-----------------111111111111111111111111-");List select1 = getSuidRich().select(new Org(), cmd);  //语句1:这句会调用数据库return new Org(org.getOrgName());}).collect(Collectors.toList());问题:语句1调用的方法有条词句:private static ThreadLocal>> sqlPreValueLocal;经过orgs00.parallelStream().map()) 执行调用语句1,sqlPreValueLocal是同一个本地变量吗?

并行流场景下使用 ThreadLocal, sqlPreValueLocal = new InheritableThreadLocal<>(); //语句1

或者

sqlPreValueLocal = new ThreadLocal<>();//语句2

用语句1有时上下文丢失,但用语句2不会, 可以解释为什么吗?

在并行流场景下,使用InheritableThreadLocalThreadLocal可能会出现不同的行为,导致上下文丢失的问题。下面我会解释一下为什么语句1有时会出现上下文丢失,而语句2不会。

  1. 使用InheritableThreadLocal(语句1):

InheritableThreadLocal会使子线程继承父线程中的ThreadLocal变量的值。在并行流中,由于线程池的调度机制,父线程有可能会参与到并行流线程池的调度。如果父线程的上下文在某个时刻被清理,那么后续拷贝到子线程的上下文可能为空,导致上下文丢失的问题。这种情况可能会导致语句1有时会出现上下文丢失的情况。

  1. 使用ThreadLocal(语句2):

ThreadLocal不具备继承性,子线程不会继承父线程中的ThreadLocal变量的值。因此,在并行流中使用ThreadLocal时,不会出现父线程上下文被清理导致的上下文丢失问题。每个线程都有自己独立的ThreadLocal副本,不会受到父线程的影响。

综上所述,使用InheritableThreadLocal在并行流场景下可能会出现上下文丢失的问题,而使用ThreadLocal则不会受到这种影响。因此,如果在并行流中需要使用ThreadLocal来传递上下文,可以考虑使用ThreadLocal而不是InheritableThreadLocal,以避免上下文丢失的情况。

原因在于并行流的设计比较特殊,父线程也有可能参与到并行流线程池的调度,那如果 方法被父线程执行,那么父线程的上下文会被清理。导致后续拷贝到子线程的上下文都为 null,同样产生丢失上下文的问题。

以上结论不保证十分准确。

但有下面的测试实验结论:

// 1. parallelStream().map + sqlPreValueLocal = new InheritableThreadLocal<>();

// 很容易出现: No value specified for parameter 1

// 2. parallelStream().map + sqlPreValueLocal = new ThreadLocal<>();

// 不会出现: No value specified for parameter 1

// 2.5 stream().map + sqlPreValueLocal = new ThreadLocal<>();

// 不会出现: No value specified for parameter 1

// 1.5 stream().map + sqlPreValueLocal = new InheritableThreadLocal<>();

// 不会出现: No value specified for parameter 1

// 结论:只有1. parallelStream().map + InheritableThreadLocal 才会现现 No value specified for parameter 1

测试代码:

import java.util.List;
import java.util.stream.Collectors;import org.teasoft.bee.osql.Op;
import org.teasoft.bee.osql.api.SuidRich;
import org.teasoft.honey.osql.core.ConditionImpl;
import org.teasoft.honey.osql.shortcut.BF;//parallelStream().map并行流测ORM
public class StreamSelectTest3 {public static void main(String[] args) {List<Org> orgs00 = getSuidRich().select(new Org()); //约50条记录//		1. parallelStream().map + sqlPreValueLocal = new InheritableThreadLocal<>();
//		很容易出现: No value specified for parameter 1//			2. parallelStream().map + sqlPreValueLocal = new ThreadLocal<>();
//			不会出现: No value specified for parameter 1//			2.5 stream().map + sqlPreValueLocal = new ThreadLocal<>();
//			不会出现: No value specified for parameter 1//			1.5 stream().map + sqlPreValueLocal = new InheritableThreadLocal<>();
//			不会出现: No value specified for parameter 1//		 结论:只有1. parallelStream().map + InheritableThreadLocal 才会现现   No value specified for parameter 1// 直接在map中进行数据库查询
//	        List<Org> orgs = orgs00.stream()List<Org> orgs = orgs00.parallelStream() // 这个才会.map(org -> {// 从数据库中获取 Org 对象的操作// 这里可以是实际的数据库查询操作ConditionImpl cmd = new ConditionImpl();cmd.op("orgName", Op.eq, org.getOrgName());System.out.println("-----------------111111111111111111111111-");List<Org> select1 = getSuidRich().select(new Org(), cmd);return new Org(org.getOrgName());}).collect(Collectors.toList());orgs.forEach(org -> System.out.println("Org name: " + org.getOrgName()));}static SuidRich getSuidRich() {return BF.getSuidRich();}
}

多线程测试测没有发现相应问题。 

import java.util.List;import org.teasoft.bee.osql.api.SuidRich;
import org.teasoft.honey.osql.shortcut.BF;public class ThreadSelectTest2 extends Thread{//	static SuidRich suidRich=BF.getSuidRich();public static void main(String[] args) throws Exception{ThreadSelectTest2 test[]=new ThreadSelectTest2[5];for (int i = 0; i < test.length; i++) {test[i]=new ThreadSelectTest2();test[i].start();}System.out.println("finished!");}//	sqlPreValueLocal = new InheritableThreadLocal<>();  //没问题 , 没那么容易连不上
//	sqlPreValueLocal = new ThreadLocal<>();   // 很容易连不上; 但没碰到  No value specified for parameter 1
//	org.teasoft.bee.osql.BeeSQLException: The driver was unable to create a connection due to an inability to establish the client portion of a socket.
//	This is usually caused by a limit on the number of sockets imposed by the operating system. This limit is usually configurable. 
//	For Unix-based platforms, see the manual page for the 'ulimit' command. Kernel or system reconfiguration may also be required.public void run() {SuidRich suidRich=BF.getSuidRich();List<Org> listOrg=suidRich.select(new Org()); //约50条for (int i = 0; i < 2; i++) {for (int j = 0; j < listOrg.size(); j++) {Org org = new Org(listOrg.get(i).getOrgName()); //拿外层每一条的值 又去查一遍;只是测试性能,不考虑业务正确与否suidRich.select(org);}}}}

http://www.lryc.cn/news/274220.html

相关文章:

  • 【C++期末编程题题库】代码+详解18道
  • 一种DevOpts的实现方式:基于gitlab的CICD(一)
  • nodejs和vuejs的区别
  • 16、Kubernetes核心技术 - 节点选择器、亲和和反亲和
  • 面试算法96:字符串交织
  • 什么是Vue.js的响应式系统(reactivity system)?如何实现数据的双向绑定?
  • 力扣labuladong一刷day52天LRU算法
  • CCNP课程实验-06-EIGRP-Trouble-Shooting
  • 判断完全数-第11届蓝桥杯省赛Python真题精选
  • 【Bootstrap5学习 day12】
  • 算法训练第五十九天|503. 下一个更大元素 II、42. 接雨水
  • mysql之数据类型、建表以及约束
  • 复试 || 就业day04(2024.01.05)项目一
  • 华为机试真题实战应用【赛题代码篇】-最小传输时延(附python、C++和JAVA代码实现)
  • C++ 运算符重载
  • vue3学习 【2】vite起步和开发工具基本配置
  • 计算机创新协会冬令营——暴力枚举题目06
  • 单片机快速入门
  • Eureka相关问题及答案(2024)
  • Django 7 实现Web便签
  • Jenkins集成部署java项目
  • FFmpeg之——获取上传视频的尺寸(长、宽)
  • Ajax学习
  • 排序算法——关于快速排序的详解
  • 序言:《未来已来》
  • 【Spring实战】22 Spring Actuator 入门
  • JSON安全性
  • spring-boot-maven插件repackage(goal)的那些事
  • ubuntu的boot分区被删除恢复
  • 【userfaultfd 条件竞争】starCTF2019 - hackme