Skip to content

CompltetableFuture结合Ttl 未使用agent方式 也拿到了ttl中的value #279

Closed
@biuabiu

Description

@biuabiu

使用方式/环境信息:

  • jdk 1.8
  • 未使用TTL agent
  • 需要依赖lombok、ttl
  • 仅使用了CompltetableFuture 和 ttl

源码

20210609145410

CompletableFutureTest2.zip

问题说明:

看日志跨线程怎么还是拿到了数据?

Activity

oldratlee

oldratlee commented on Jun 14, 2021

@oldratlee
Member

问题说明:

看日志跨线程怎么还是拿到了数据?

直接原因说明

原因是:

TransmittableThreadLocal本身也是InheritableThreadLocal,有Inheritable能力:
可以在新建线程时,传递父子线程的ThreadLocal值。

上面的代码例子,用InheritableThreadLocal运行的结果是一样的。
即是InheritableThreadLocal的功能的理解使用,可以认为与TransmittableThreadLocal(的加强)无关。

对于上面代码运行例子的具体说明如下:
check操作的线程(上面运行对应test-business-4)由cache线程(上面运行对应test-business-1)创建的。
# Executors.newFixedThreadPoolThreadPoolExecutor的线程创建是Lazy的。

基于Inheritable能力做上下文传递是Bug

因为Inheritable能力只有在新建线程时,才会传递ThreadLocal值,
所以上面传递的能力是有Bug的。

Bug的场景 说明如下:

  • 当提交任务给在线程池中已经创建的线程时
    • 不会再传递ThreadLocal值,传递失效
    • 已经创建的线程,持有还是之前Inheritable过来的线程,串号了

可以通过大量提交任务(实际线上运行的业务逻辑 会持续提交任务),
就可以发现上面 失效/串号Bug了。

上面的Bug其实就是为什么需要有TransmittableThreadLocal这个加强实现的一个原因。
更多说明 可以参见文档:

确认是由Inheritable能力引起的验证实验

为了确认上面问题的判断,可以做下面的实验: @biuabiu

  • 实现一个任务,包含下面的逻辑:
    • Sleep一段时间(如100ms)。用于扩充线程。
    • 清除transmittableThreadLocalOfObject.remove。避免Inheritable能力的影响。
  • 异步提交多次(大于线程池大小,上面示例代码里是8)任务 到Executors.newFixedThreadPoolThreadPoolExecutor
    • 将线程池扩充起来,以保证后续在线程池中运行任务中,不会再新建线程操作
  • 然后再运行上面的代码逻辑

你实验验证一下,如有问题欢迎继续讨论。 @biuabiu


没说明清楚的地方,也欢迎继续讨论。😄

PS

TTL提供了 关闭 Inheritable能力的解法:
(可能要理解一下 这个解法及其背景原因,有些复杂性。 😄 )

self-assigned this
on Jun 15, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

❓questionFurther information is requested

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

    Development

    No branches or pull requests

      Participants

      @oldratlee@biuabiu

      Issue actions

        CompltetableFuture结合Ttl 未使用agent方式 也拿到了ttl中的value · Issue #279 · alibaba/transmittable-thread-local