alibaba / transmittable-thread-local

📌 a missing Java std lib(simple & 0-dependency) for framework/middleware, provide an enhanced InheritableThreadLocal that transmits values between threads even using thread pooling components.
https://github.com/alibaba/transmittable-thread-local
Apache License 2.0
7.59k stars 1.69k forks source link

如何实现完成线程传递后的初始化操作 #383

Closed fengzidk closed 2 years ago

fengzidk commented 2 years ago

根据业务场景,在线程传递完成后需要进行一些默认的初始化操作 例如: 根据传递的ThreadLocal值 初始化子线程的数据源信息

目前TransmittableThreadLocal.set()方法不允许被重写,如何在不修改源码的前提下实现?

oldratlee commented 2 years ago

TransmittableThreadLocalThreadLocal/InheritableThreadLocal。 (aka. TransmittableThreadLocalThreadLocal/InheritableThreadLocal的子类。)

线程传递时如何初始化

目前TransmittableThreadLocal.set()方法不允许被重写,如何在不修改源码的前提下实现?

@fengzidk 线程传递的初始化:

关于初始化方法

相关的初始化方法还有 [ThreadLocal#initialValue()](https://docs.oracle.com/javase/10/docs/api/java/lang/ThreadLocal.html#initialValue())。

这3个初始化方法 的区别是 做初始化操作的 生命周期时间点/触发时间 不同:

  1. ThreadLocal#initialValue()
    • ThreadLocal没有值时,取值(ThreadLocal#get()方法)触发初始化。
    • 下面是一些注意点
    • ThreadLocal#initialValue()Lazy的; 即创建ThreadLocal实例时,并不会触发ThreadLocal#initialValue()调用。
    • 先设置值(ThreadLocal#set(T))再取值,则不会触发调用ThreadLocal#initialValue();因为已经有值了。 即使设置的是null,也不会触发。
    • 调用ThreadLocal#remove()再取值,会触发调用ThreadLocal#initialValue();因为没有值了。
  2. InheritableThreadLocal#childValue(T)
    • 创建新线程时,用于初始化子线程的InheritableThreadLocal值。
  3. TransmittableThreadLocal#copy(T)
    • 传递时,用于初始化 在任务(如TtlRunnable)执行中的TransmittableThreadLocal值。

不同初始化方法是独立正交的

这里可能值得强调一下这3个初始化方法的容易被忽视的地方:3个方法是独立正交的。 (好的系统设计应该这样,不同功能独立正交;系统大的功能 是通过 方便地组合/使用更小的功能 来实现。)

展开一些的说明如下:

更多相关讨论参见

子线程remove/set操作不应该影响父线程。更进一步表述:

  • ThreadLocal值在不同线程之间是独立的(这其实是ThreadLocal的定义和命名)。
    • 即 更新操作 不会影响 别的线程的ThreadLocal值,这是正确的功能。
  • 在约定的时机会 传递
    • 这样的传递时机,作为功能 命名成如 InheritableTransmittable

系统设计最佳实践:结合业务扩展重写初始化方法,而不是用无业务含义的缺省实现

ThreadLocal的使用中,看到初始化方法的扩展重写没有得到应有的重视与理解使用。

即初始化方法没有重写,结果是在用无业务含义的缺省实现:

这样的做法,导致 上面提到的『通过ThreadLocal#set(T)方法来完成初始化工作』。结果会导致:

写一个示意Demo可以这样;但作为实际系统实现,

强烈推荐:结合业务与不同初始化方法的生命周期时间点 扩展重写好 相应的初始化方法。

用于重写初始化方法的TransmittableThreadLocal便利工具方法

TransmittableThreadLocal提供了下面3个静态工具方法withInitial*,可以方便地重写这些初始化方法的实现逻辑: (如果没有静态工具方法withInitial*,就要写一个TransmittableThreadLocal子类来重写初始化方法,相对不方便)


@fengzidk 具体展开的更多说明,你可以先了解一下ThreadLocal及其子类。比如看看

如果你用上面的初始化方法不能实现你的需求, 可以贴一下示例代码 并展开说明需求与问题,我们一起看一下 ❤️ @fengzidk