@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-07-13 11:35:10 +08:00
用 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
lancelee01 2022-07-13 11:39:51 +08:00
@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-07-13 12:13:10 +08:00
@JinTianYi456 有没有可能和这个异步有关
|
5
JinTianYi456 OP @falsemask #4 只保留 @Configuration 也一样
|
6
wolfie 2022-07-13 13:49:06 +08:00
你这被 sleuth 增强过吧,看看 LazyTraceThreadPoolTaskExecutor#toString 有没有自定义,或者初始化时候有没有使用匿名内部类覆盖。
|
7
JinTianYi456 OP @wolfie #6 看过了,没有(它这输出样式就基本断定 Object 里的
|
8
Aruforce 2022-07-13 14:28:30 +08:00
查下这个类是不是被 spring 加载的。。如果不是。。 @Bean 相关的的 methodInvokeInterceptor 是不生效的
|
9
Aruforce 2022-07-13 14:29:13 +08:00
@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-07-13 15:07:30 +08:00
@JinTianYi456 System.err.println(executor.getClass().getClassLoader().getClass().getName());
|
12
JinTianYi456 OP @Aruforce #11 java.net.URLClassLoader
|
13
zmal 2022-07-13 15:25:48 +08:00
一模一样的代码试了下:
1835794313 org.springframework.cloud.sleuth.instrument.async.LazyTraceThreadPoolTaskExecutor@549ac12c 1835794313 org.springframework.cloud.sleuth.instrument.async.LazyTraceThreadPoolTaskExecutor@549ac12c |
14
BanGanExpert 2022-07-13 15:38:22 +08:00
把你自己的 test 生成的 class 字节码反编译或者直接贴出来分析,这个明显属于异常行为了呀,现在只有的代码片段理论上看不可能出现这种行为
|
15
BanGanExpert 2022-07-13 15:40:46 +08:00
如果反编译分析出来没有问题,就必须自己一行行 debug 去看下 JVM 运行时的行为了
|
16
ArthurTsang 2022-07-13 16:16:02 +08:00
你的确是有 2 个 ThreadPoolTaskExecutor 对象吧? 一个是 @Bean, 一个是手动调用方法 new 出来的
|
17
JinTianYi456 OP @ArthurTsang #16 看 test code ,没有 2 个啊。再说 Configuration#proxyBeanMethods
|
18
lancelee01 2022-07-13 17:07:20 +08:00
@ArthurTsang 楼主用的的确没有用 Spring 管理的 bean ,而是通过手动调用 asyncServiceExecutor 方法创建的,但是两次打印的还是同一个对象
|
19
lancelee01 2022-07-13 17:08:07 +08:00
@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-07-13 17:18:02 +08:00
@JinTianYi456 看了下 sleuth 的源码... sleuth 做了线程池 bean name = taskExecutor 的 wrap 。
看着像是 DefaultAsyncConfigurerSupport 这个类 做的业务逻辑。。 |
22
lancelee01 2022-07-13 17:20:24 +08:00
@JinTianYi456 你把你的工程打个包发下吧
|
23
yhvictor 2022-07-13 17:39:32 +08:00
查了下 source code: https://github.com/openjdk/jdk/blob/6e18883d8ffd9a7b7d495da05e9859dc1d1a2677/src/java.base/share/classes/java/lang/Object.java#L257
估计是对象是一个对象,但是在两次之间发生了 field 的修改,导致 hashcode 不同 |
24
siweipancc 2022-07-14 11:40:29 +08:00 via iPhone
做个实验,Object.class.cast(executor) .toString()
|
25
JinTianYi456 OP |
26
JinTianYi456 OP |
27
falsemask 2022-07-14 23:14:58 +08:00
@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-07-14 23:21:45 +08:00
收藏了,debug 出来踢我一下
|
30
zmal 2022-07-15 11:44:41 +08:00 1
debug 了一下大概明白了。
spring 中注入的 ThreadPoolTaskExecutor 只有一个。但引入了 sleuth 后,它劫持了 executor 的调用。executor 注入 Example.class 后每次调用 executor 内的方法时,都会用这个 executor 包裹一层 LazyTraceThreadPoolTaskExecutor 生成一个新的 LazyTraceThreadPoolTaskExecutor 对象。 |
31
zmal 2022-07-15 11:51:23 +08:00
这应该算是早期 LazyTraceThreadPoolTaskExecutor 的一个 bug 。
用最新版的 sleuth 能看到里边多了个 cache ,保存了 ThreadPoolTaskExecutor 和 LazyTraceThreadPoolTaskExecutor 的映射,用来保证同一个 ThreadPoolTaskExecutor 只生成一个 LazyTraceThreadPoolTaskExecutor 。 |
32
nothingistrue 2022-07-15 12:28:21 +08:00
既然是已经用了 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-07-15 23:07:53 +08:00
@JinTianYi456
#30 #31 这个在 sleuth 的 issue 里有对应的提问。 但不能解释为什么 每次执行 System.err.println(executor); executor 都不同。 |