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

TransmittableThreadLocal 在 Capture/Replay 操作时使用 HashMap 的问题 #136

Closed cubika closed 5 years ago

cubika commented 5 years ago

请问 TransmittableThreadLocal 在线程切换时进行上下文备份和回放,为什么使用 HashMap 而非 WeakHashMap 进行存储?

对应代码:

这样把原来是弱引用的 key 变成了强引用,可能导致父线程已经执行完了,但由于子线程使用了 TransmittableThreadLocal 变相延长了生命周期,restore 返回之前无法被回收。

// 在父线程中设置
TransmittableThreadLocal<String> parent = null;

for (int i = 0; i < 100; i++) {
 // 假设不小心忽略了remove操作
  parent = new TransmittableThreadLocal<String>();
  parent.set("value-set-in-parent");
}
TransmittableThreadLocal<String> finalTtl = parent;

// =====================================================

// 在子线程中可以读取,值是"value-set-in-parent" 
// 此时前99个按理说已经没有强引用可以被回收了,回收后如果触发到清理逻辑,对应的value也会被删除
// 但是由于 capture 等操作的存在,前99个key变成强引用,子线程执行中时是无法被回收的
String value = finalTtl.get();
oldratlee commented 5 years ago

TransmittableThreadLocal 在线程切换时进行上下文备份和回放,为什么使用 HashMap 而非 WeakHashMap 进行存储?

Capture + Replay + Restore (即CRR) 是为了完成 业务 上下文的传递,所以一定要传递成功。

如果CRR过程用的是WeakHashMap,则传递过程中 可能会被释放掉;即可能 传递失败,即是 Bug。 @cubika

可能会被释放掉 的原因是:


把原来是弱引用的 key 变成了强引用,可能导致父线程已经执行完了,但由于子线程使用了 TransmittableThreadLocal 变相延长了生命周期,restore 返回之前无法被回收。

因为 子线程 要使用上下文,所以保持 上下文的生命周期(即不被回收) 是 预期的,否则即上下文传递失败。

在子线程中可以读取,值是"value-set-in-parent" 此时前99个按理说已经没有强引用可以被回收了,回收后如果触发到清理逻辑,对应的value也会被删除 但是由于 capture 等操作的存在,前99个key变成强引用,子线程执行中时是无法被回收的

上下文传递后,任务对象的生命周期由任务本身的需求来决定,所有任务对象释放 且 父线程不再持有上下文(即上下文没有人需要使用了),上下文无强引用会被释放。

即『此时前99个按理说已经没有强引用可以被回收了,回收后如果触发到清理逻辑,对应的value也会被删除』这个不能成立,因为任务还需要使用上下文,对应的value不能被删除。 这点以上面的『延迟执行的任务』作为Case比较容易理解这个设计方式。

cubika commented 5 years ago

理解了,因为是异步的,子线程并不强保证执行顺序(即使代码看起来是立即执行的),可能存在延迟执行的情况,这时如果父线程上下文被回收,子线程就拿不到需要的数据,从而产生 bug

oldratlee commented 4 years ago

所有权 确实是个『费脑子』的问题。

经过仔细的分析:


实现代码已经改好了,并发布了版本2.11.0https://github.com/alibaba/transmittable-thread-local/releases/tag/v2.11.0

  • use WeakHashMap instead of HashMap for capture/backup snapshot 🚢
    • TTL do NOT have the ownership of thread local/snapshot!
    • more gc friendly, avoid potential memory leak!