
@Configuration @EnableAsync @Slf4j public class ExecutorConfig { @Bean("taskExecutor") public ThreadPoolTaskExecutor asyncServiceExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); return executor; } } // test code var executor = executorConfig.asyncServiceExecutor(); System.err.println(System.identityHashCode(executor)); System.err.println(executor); System.err.println(System.identityHashCode(executor)); System.err.println(executor); // output 2016053161 org.springframework.cloud.sleuth.instrument.async.LazyTraceThreadPoolTaskExecutor@62730eda 2016053161 org.springframework.cloud.sleuth.instrument.async.LazyTraceThreadPoolTaskExecutor@65d849e0 1 lancelee01 2022 年 7 月 13 日 用 Java 试了一下,是同一个对象。 ``` ExecutorService pool = new ThreadPoolExecutor(8, 8, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); System.out.println(System.identityHashCode(pool)); System.out.println(pool); System.out.println(System.identityHashCode(pool)); System.out.println(pool); ``` ``` ``` |
2 /div> lancelee01 2022 年 7 月 13 日 @lancelee01 1639705018 java.util.concurrent.ThreadPoolExecutor@61bbe9ba[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0] 1639705018 java.util.concurrent.ThreadPoolExecutor@61bbe9ba[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0] |
3 JinTianYi456 OP @lancelee01 #1 我也觉得是一样的,但你没按照我的环境复现,比如 @Configuration ExecutorConfig ,以及是 LazyTraceThreadPoolTaskExecutor 、、因为我也不知道是哪里影响到了 toString 的结果 |
4 falsemask 2022 年 7 月 13 日 @JinTianYi456 有没有可能和这个异步有关 |
5 JinTianYi456 OP @falsemask #4 只保留 @Configuration 也一样 |
6 wolfie 2022 年 7 月 13 日 你这被 sleuth 增强过吧,看看 LazyTraceThreadPoolTaskExecutor#toString 有没有自定义,或者初始化时候有没有使用匿名内部类覆盖。 |
7 JinTianYi456 OP @wolfie #6 看过了,没有(它这输出样式就基本断定 Object 里的 |
9 Aruforce 2022 年 7 月 13 日 @Aruforce 或者你看下 @Configuration proxyBeanMethods 默认行为是不是 true |
10 JinTianYi456 OP @Aruforce #9 System.err.println(executor.getClass().getName()); org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor$$EnhancerBySpringCGLIB$$b9d128b3 |
11 Aruforce 2022 年 7 月 13 日 @JinTianYi456 System.err.println(executor.getClass().getClassLoader().getClass().getName()); |
12 JinTianYi456 OP @Aruforce #11 java.net.URLClassLoader |
13 zmal 2022 年 7 月 13 日 一模一样的代码试了下: 1835794313 org.springframework.cloud.sleuth.instrument.async.LazyTraceThreadPoolTaskExecutor@549ac12c 1835794313 org.springframework.cloud.sleuth.instrument.async.LazyTraceThreadPoolTaskExecutor@549ac12c |
14 BanGanExpert 2022 年 7 月 13 日 把你自己的 test 生成的 class 字节码反编译或者直接贴出来分析,这个明显属于异常行为了呀,现在只有的代码片段理论上看不可能出现这种行为 |
15 BanGanExpert 2022 年 7 月 13 日 如果反编译分析出来没有问题,就必须自己一行行 debug 去看下 JVM 运行时的行为了 |
16 ArthurTsang 2022 年 7 月 13 日 你的确是有 2 个 ThreadPoolTaskExecutor 对象吧? 一个是 @Bean, 一个是手动调用方法 new 出来的 |
17 JinTianYi456 OP @ArthurTsang #16 看 test code ,没有 2 个啊。再说 Configuration#proxyBeanMethods |
18 lancelee01 2022 年 7 月 13 日 @ArthurTsang 楼主用的的确没有用 Spring 管理的 bean ,而是通过手动调用 asyncServiceExecutor 方法创建的,但是两次打印的还是同一个对象 |
19 lancelee01 2022 年 7 月 13 日 @JinTianYi456 改成 Spring-boot 环境两次还是一样,你是不是看错了啊。 ApplicationContext cOntext= SpringApplication.run(App.class); var pool = context.getBean(ExecutorConfig.class).asyncServiceExecutor(); |
20 JinTianYi456 OP @lancelee01 #18 是 spring 管理的,请看 Configuration#proxyBeanMethods 的说明 2. 即使 `手动调用 asyncServiceExecutor` 我也只调用了一次啊 |
21 Aruforce 2022 年 7 月 13 日 @JinTianYi456 看了下 sleuth 的源码... sleuth 做了线程池 bean name = taskExecutor 的 wrap 。 看着像是 DefaultAsyncConfigurerSupport 这个类 做的业务逻辑。。 |
22 lancelee01 2022 年 7 月 13 日 @JinTianYi456 你把你的工程打个包发下吧 |
23 yhvictor 2022 年 7 月 13 日 查了下 source code: https://github.com/openjdk/jdk/blob/6e18883d8ffd9a7b7d495da05e9859dc1d1a2677/src/java.base/share/classes/java/lang/Object.java#L257 估计是对象是一个对象,但是在两次之间发生了 field 的修改,导致 hashcode 不同 |
24 siweipancc 2022 年 7 月 14 日 via iPhone 做个实验,Object.class.cast(executor) .toString() |
25 JinTianYi456 OP |
26 JinTianYi456 OP |
27 falsemask 2022 年 7 月 14 日 @JinTianYi456 我在 toString()方法打了断点,发现这个线程池是通过代理生成的,最后的 toString()方法也是通过代理执行的,最终调用了 invoke0(method, obj, args),然后一个 native 方法,最后 Object 的 toString(),这里有个 args 对象,每次传的值都不相等,感觉可能是这里的问题(不过也不太确定,毕竟 toString 方法是没有参数的) https://s3.bmp.ovh/imgs/2022/07/14/751ee9ec7e911119.png |
28 redford42 2022 年 7 月 14 日 收藏了,debug 出来踢我一下 |
30 zmal 2022 年 7 月 15 日 debug 了一下大概明白了。 spring 中注入的 ThreadPoolTaskExecutor 只有一个。但引入了 sleuth 后,它劫持了 executor 的调用。executor 注入 Example.class 后每次调用 executor 内的方法时,都会用这个 executor 包裹一层 LazyTraceThreadPoolTaskExecutor 生成一个新的 LazyTraceThreadPoolTaskExecutor 对象。 |
31 zmal 2022 年 7 月 15 日 这应该算是早期 LazyTraceThreadPoolTaskExecutor 的一个 bug 。 用最新版的 sleuth 能看到里边多了个 cache ,保存了 ThreadPoolTaskExecutor 和 LazyTraceThreadPoolTaskExecutor 的映射,用来保证同一个 ThreadPoolTaskExecutor 只生成一个 LazyTraceThreadPoolTaskExecutor 。 |
32 nothingistrue 2022 年 7 月 15 日 既然是已经用了 Spring ,你好像还用了其他的 AOP ,那么 var executor = executorConfig.asyncServiceExecutor(); 弄出来的 executor ,就不一定是前面代码上的 “new ThreadPoolTaskExecutor();”。或者说,executor 对象的类型,不一定是 ThreadPoolTaskExecutor 。这时候用 ThreadPoolTaskExecutor 的 hashCode 和 toString 方法推断的测试结果,可能不是实际结果。 |
33 JinTianYi456 OP @nothingistrue #32 本问题和`那么 var executor = executorConfig.asyncServiceExecutor(); 弄出来的 executor ,就不一定是前面代码上的 “new ThreadPoolTaskExecutor();”`这里,是自己调用`executorConfig.asyncServiceExecutor()`还是 注入 bean `taskExecutor` 是没关系的。test code 里是和这没关系的。 ----------------- 另外请看 Configuration#proxyBeanMethods 的说明,它就是同一个 bean (按默认配置的话 |
34 JinTianYi456 OP 感谢 zmal , 大家看 #30 #31 即可,谢谢 |
35 DonaldY 2022 年 7 月 15 日 @JinTianYi456 #30 #31 这个在 sleuth 的 issue 里有对应的提问。 但不能解释为什么 每次执行 System.err.println(executor); executor 都不同。 |