Closed songxiaosheng closed 1 year ago
new Thread
),触发的是InheritableThreadLocal.childValue()
方法。initialValue()
、childValue()
、copy()
,其触发有确定的时机。
(展开说明见文后的引用内容)具体你的需求,对应的用法 是
重写(override
)InheritableThreadLocal.childValue()
以代理到copy()
方法: @songxiaosheng 💕
protected TtlObject childValue(TtlObject parentValue) {
return copy(parentValue);
}
PS:
Agent
使用方式 与copy
方法 是独立正交的。
Agent
方式只是在使用上的便利,省去API
方式要写的代码行(如调用TtlRunnable.get()
、TtlExecutors.getTtlExecutorService()
)。
更多这方面的使用与设计及其原因的说明 在已有的 issue 中有展开讨论: @songxiaosheng
摘一些内容如下:
关于初始化方法
相关的初始化方法还有 [
ThreadLocal#initialValue()
](https://docs.oracle.com/javase/10/docs/api/java/lang/ThreadLocal.html#initialValue())。这3个初始化方法 的区别是 做初始化操作的 生命周期时间点/触发时间 不同:
ThreadLocal#initialValue()
ThreadLocal
没有值时,取值(ThreadLocal#get()
方法)触发初始化。- 下面是一些注意点:
ThreadLocal#initialValue()
是 Lazy的; 即创建ThreadLocal
实例时,并不会触发ThreadLocal#initialValue()
调用。- 先设置值(
ThreadLocal#set(T)
)再取值,则不会触发调用ThreadLocal#initialValue()
;因为已经有值了。 即使设置的是null
,也不会触发。- 调用
ThreadLocal#remove()
再取值,会触发调用ThreadLocal#initialValue()
;因为没有值了。InheritableThreadLocal#childValue(T)
- 创建新线程时,用于初始化子线程的
InheritableThreadLocal
值。TransmittableThreadLocal#copy(T)
- 传递时,用于初始化 在任务(如
TtlRunnable
)执行中的TransmittableThreadLocal
值。不同初始化方法是独立正交的
这里可能值得强调一下这3个初始化方法的容易被忽视的地方:3个方法是独立正交的。 (好的系统设计应该这样,不同功能独立正交;系统大的功能 是通过 方便地组合/使用更小的功能 来实现。)
展开一些的说明如下:
- 因为生命周期时间点/触发时间 不同,各自独立发挥作用。
- 子类包含进来的父类初始化方法,功能不变。
InheritableThreadLocal
是ThreadLocal
的子类,包含的ThreadLocal#initialValue()
功能不变。TransmittableThreadLocal
是InheritableThreadLocal
的子类,包含的ThreadLocal#initialValue()
、InheritableThreadLocal#childValue(T)
功能不变。系统设计最佳实践:结合业务扩展重写初始化方法,而不是用无业务含义的缺省实现
在
ThreadLocal
的使用中,看到初始化方法的扩展重写没有得到应有的重视与理解使用。即初始化方法没有重写,结果是在用无业务含义的缺省实现:
ThreadLocal#initialValue()
:返回null
InheritableThreadLocal#childValue(T)
:直接返回入参TransmittableThreadLocal#copy(T)
:直接返回入参这样的做法,导致 上面提到的『通过
ThreadLocal#set(T)
方法来完成初始化工作』。结果会导致:
- 需要用户主动调用
ThreadLocal#set(T)
来,用户使用麻烦/困难。- 通过用户调用
ThreadLocal#set(T)
来做初始化操作,因为语义不对应(即系统设计错误), 这样的实现方式往往会引发隐晦偶现的业务逻辑Bug!写一个示意Demo可以这样;但作为实际系统实现,
强烈推荐:结合业务与不同初始化方法的生命周期时间点 扩展重写好 相应的初始化方法。
用于重写初始化方法的
TransmittableThreadLocal
便利工具方法
TransmittableThreadLocal
提供了下面3个静态工具方法withInitial*
,可以方便地重写这些初始化方法的实现逻辑: (如果没有静态工具方法withInitial*
,就要写一个TransmittableThreadLocal
子类来重写初始化方法,相对不方便)
感谢回复🌺,我研究下
描述
使用TransmittableThreadLocal时候对普通的Thread会实现自动拷贝线程数据,如果是ThreadLocal中的数据是对象只能进行深拷贝,无法触发重写的copy方法,如果是否agent中可以增加对普通线程的处理为Runnable增加包装让普通线程传递数据时候也触发copy方法
期望
期望普通线程的传递也能触发重写的copy方法,这样可以统一管控对象传递过程中的数据内容
样例代码
测试代码如下: