JinTianYi456
V2EX  ›  Java

并未做任何处理,但两次 toString 结果不一致,为啥?

  •  
  •   JinTianYi456 · Jul 13, 2022 · 5934 views
    This topic created in 1573 days ago, the information mentioned may be changed or developed.
    @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
    
    35 replies  •  2022-07-15 23:07:53 +08:00
    lancelee01
    lancelee01
    Jul 13, 2022
    1
    用 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);
    ```
    ```

    ```
    lancelee01
    lancelee01
    Jul 13, 2022
    2
    @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]
    JinTianYi456
    JinTianYi456
    OP
    Jul 13, 2022
    3
    @lancelee01 #1 我也觉得是一样的,但你没按照我的环境复现,比如 @Configuration ExecutorConfig ,以及是 LazyTraceThreadPoolTaskExecutor 、、因为我也不知道是哪里影响到了 toString 的结果
    falsemask
    falsemask
    Jul 13, 2022
    4
    @JinTianYi456 有没有可能和这个异步有关
    JinTianYi456
    JinTianYi456
    OP
    Jul 13, 2022
    5
    @falsemask #4 只保留 @Configuration 也一样
    wolfie
    wolfie
    Jul 13, 2022
    6
    你这被 sleuth 增强过吧,看看 LazyTraceThreadPoolTaskExecutor#toString 有没有自定义,或者初始化时候有没有使用匿名内部类覆盖。
    JinTianYi456
    JinTianYi456
    OP
    Jul 13, 2022
    7
    @wolfie #6 看过了,没有(它这输出样式就基本断定 Object 里的
    Aruforce
    Aruforce
    Jul 13, 2022
    8
    查下这个类是不是被 spring 加载的。。如果不是。。 @Bean 相关的的 methodInvokeInterceptor 是不生效的
    Aruforce
    Aruforce
    Jul 13, 2022
    9
    @Aruforce 或者你看下 @Configuration proxyBeanMethods 默认行为是不是 true
    JinTianYi456
    JinTianYi456
    OP
    Jul 13, 2022
    10
    @Aruforce #9 System.err.println(executor.getClass().getName());
    org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor$$EnhancerBySpringCGLIB$$b9d128b3
    Aruforce
    Aruforce
    Jul 13, 2022
    11
    @JinTianYi456 System.err.println(executor.getClass().getClassLoader().getClass().getName());
    JinTianYi456
    JinTianYi456
    OP
    Jul 13, 2022
    12
    @Aruforce #11 java.net.URLClassLoader
    zmal
    zmal
    Jul 13, 2022
    13
    一模一样的代码试了下:

    1835794313
    org.springframework.cloud.sleuth.instrument.async.LazyTraceThreadPoolTaskExecutor@549ac12c
    1835794313
    org.springframework.cloud.sleuth.instrument.async.LazyTraceThreadPoolTaskExecutor@549ac12c
    BanGanExpert
    BanGanExpert
    Jul 13, 2022
    14
    把你自己的 test 生成的 class 字节码反编译或者直接贴出来分析,这个明显属于异常行为了呀,现在只有的代码片段理论上看不可能出现这种行为
    BanGanExpert
    BanGanExpert
    Jul 13, 2022
    15
    如果反编译分析出来没有问题,就必须自己一行行 debug 去看下 JVM 运行时的行为了
    ArthurTsang
    ArthurTsang
    Jul 13, 2022
    16
    你的确是有 2 个 ThreadPoolTaskExecutor 对象吧? 一个是 @Bean, 一个是手动调用方法 new 出来的
    JinTianYi456
    JinTianYi456
    OP
    Jul 13, 2022
    17
    @ArthurTsang #16 看 test code ,没有 2 个啊。再说 Configuration#proxyBeanMethods
    lancelee01
    lancelee01
    Jul 13, 2022
    18
    @ArthurTsang 楼主用的的确没有用 Spring 管理的 bean ,而是通过手动调用 asyncServiceExecutor 方法创建的,但是两次打印的还是同一个对象
    lancelee01
    lancelee01
    Jul 13, 2022
    19
    @JinTianYi456 改成 Spring-boot 环境两次还是一样,你是不是看错了啊。
    ApplicationContext context = SpringApplication.run(App.class);
    var pool = context.getBean(ExecutorConfig.class).asyncServiceExecutor();
    JinTianYi456
    JinTianYi456
    OP
    Jul 13, 2022
    20
    @lancelee01 #18 是 spring 管理的,请看 Configuration#proxyBeanMethods 的说明
    2. 即使 `手动调用 asyncServiceExecutor` 我也只调用了一次啊
    Aruforce
    Aruforce
    Jul 13, 2022
    21
    @JinTianYi456 看了下 sleuth 的源码... sleuth 做了线程池 bean name = taskExecutor 的 wrap 。
    看着像是 DefaultAsyncConfigurerSupport 这个类 做的业务逻辑。。
    lancelee01
    lancelee01
    Jul 13, 2022
    22
    @JinTianYi456 你把你的工程打个包发下吧
    yhvictor
    yhvictor
    Jul 13, 2022
    23
    查了下 source code: https://github.com/openjdk/jdk/blob/6e18883d8ffd9a7b7d495da05e9859dc1d1a2677/src/java.base/share/classes/java/lang/Object.java#L257

    估计是对象是一个对象,但是在两次之间发生了 field 的修改,导致 hashcode 不同
    siweipancc
    siweipancc
    Jul 14, 2022 via iPhone
    24
    做个实验,Object.class.cast(executor) .toString()
    JinTianYi456
    JinTianYi456
    OP
    Jul 14, 2022
    26
    falsemask
    falsemask
    Jul 14, 2022
    27
    @JinTianYi456 我在 toString()方法打了断点,发现这个线程池是通过代理生成的,最后的 toString()方法也是通过代理执行的,最终调用了 invoke0(method, obj, args),然后一个 native 方法,最后 Object 的 toString(),这里有个 args 对象,每次传的值都不相等,感觉可能是这里的问题(不过也不太确定,毕竟 toString 方法是没有参数的) https://s3.bmp.ovh/imgs/2022/07/14/751ee9ec7e911119.png
    redford42
    redford42
    Jul 14, 2022
    28
    收藏了,debug 出来踢我一下
    falsemask
    falsemask
    Jul 14, 2022
    29
    @falsemask 具体也不太确定,对 cglib 不熟,明天上班有空再调一下,或者有人调出来了,踢我一下
    zmal
    zmal
    Jul 15, 2022❤️1
    30
    debug 了一下大概明白了。
    spring 中注入的 ThreadPoolTaskExecutor 只有一个。但引入了 sleuth 后,它劫持了 executor 的调用。executor 注入 Example.class 后每次调用 executor 内的方法时,都会用这个 executor 包裹一层 LazyTraceThreadPoolTaskExecutor 生成一个新的 LazyTraceThreadPoolTaskExecutor 对象。
    zmal
    zmal
    Jul 15, 2022
    31
    这应该算是早期 LazyTraceThreadPoolTaskExecutor 的一个 bug 。
    用最新版的 sleuth 能看到里边多了个 cache ,保存了 ThreadPoolTaskExecutor 和 LazyTraceThreadPoolTaskExecutor 的映射,用来保证同一个 ThreadPoolTaskExecutor 只生成一个 LazyTraceThreadPoolTaskExecutor 。
    nothingistrue
    nothingistrue
    Jul 15, 2022
    32
    既然是已经用了 Spring ,你好像还用了其他的 AOP ,那么 var executor = executorConfig.asyncServiceExecutor(); 弄出来的 executor ,就不一定是前面代码上的 “new ThreadPoolTaskExecutor();”。或者说,executor 对象的类型,不一定是 ThreadPoolTaskExecutor 。这时候用 ThreadPoolTaskExecutor 的 hashCode 和 toString 方法推断的测试结果,可能不是实际结果。
    JinTianYi456
    JinTianYi456
    OP
    Jul 15, 2022
    33
    @nothingistrue #32 本问题和`那么 var executor = executorConfig.asyncServiceExecutor(); 弄出来的 executor ,就不一定是前面代码上的 “new ThreadPoolTaskExecutor();”`这里,是自己调用`executorConfig.asyncServiceExecutor()`还是 注入 bean `taskExecutor` 是没关系的。test code 里是和这没关系的。
    -----------------
    另外请看 Configuration#proxyBeanMethods 的说明,它就是同一个 bean (按默认配置的话
    JinTianYi456
    JinTianYi456
    OP
    Jul 15, 2022
    34
    感谢 zmal , 大家看 #30 #31 即可,谢谢
    DonaldY
    DonaldY
    Jul 15, 2022
    35
    @JinTianYi456

    #30 #31 这个在 sleuth 的 issue 里有对应的提问。

    但不能解释为什么 每次执行 System.err.println(executor); executor 都不同。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   5491 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 53ms · UTC 02:06 · PVG 10:06 · LAX 18:06 · JFK 21:06
    ♥ Do have faith in what you're doing.