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