Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

请教个问题:TransmittableThreadLocal中的holder为什么不使用ThreadLocal #132

Closed
yearyeardiff opened this issue Mar 26, 2019 · 4 comments
Assignees
Labels
❓question Further information is requested

Comments

@yearyeardiff
Copy link

我对holder的理解是:保存当前线程所有的threadlocal用于在TtlRunnable.get(task)生成快照。个人觉得没有必要从使用InheritableThreadLocal类型的holder从父线程中继承,完全可以从生成的快照中恢复到holder中

@yearyeardiff
Copy link
Author

yearyeardiff commented Mar 26, 2019

还有个问题:

@Override
public void run() {
    Object captured = capturedRef.get();
    if (captured == null || releaseTtlValueReferenceAfterRun && !capturedRef.compareAndSet(captured, null)) {
        throw new IllegalStateException("TTL value reference is released after run!");
    }

    Object backup = replay(captured);
    try {
        runnable.run();
    } finally {
        restore(backup);
    }
}

从这段代码中可以看出,task所在线程每次使用的threadlocal都是从captured,和此时线程holder中的threadlocal没有关系,如果没有关系,那为什么还要备份和恢复呢?这样不会有内存泄漏吗?

@oldratlee
Copy link
Member

oldratlee commented Mar 26, 2019

TransmittableThreadLocal中的holder为什么不使用ThreadLocal

我对holder的理解是:保存当前线程所有的threadlocal用于在TtlRunnable.get(task)生成快照。
个人觉得没有必要从使用InheritableThreadLocal类型的holder从父线程中继承,
可以从生成的快照中恢复到holder中

直接给复现问题的测试Case如下, @yearyeardiff 你可以试试 :)
# 问题的整体实现流程用文字说明会啰嗦冗长,不展开解释;如果你对实现感兴趣,可以自己就着代码梳理一番即可。

期望输出 hello hello
如果holder使用ThreadLocal,输出的是hello null

import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.TtlRunnable;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo {
    public static void main(String[] args) throws Exception {
        final ExecutorService executorService = Executors.newSingleThreadExecutor();
        final TransmittableThreadLocal<String> ttl = new TransmittableThreadLocal<>();
        ttl.set("hello");

        executorService.submit(TtlRunnable.get(() -> System.out.println(ttl.get())));
        Thread.sleep(500);

        new Thread( () -> {
            // 在new线程中,执行线程池操作
            final TtlRunnable task = TtlRunnable.get(() -> System.out.println(ttl.get()));
            executorService.submit(task);
        }).start();
        Thread.sleep(500);
    }
}

PS

这个问题 提过Bug 并 修复发版,更多信息 参见 Issue #70 嵌入Thread调用的bug

@oldratlee
Copy link
Member

oldratlee commented Mar 26, 2019

从这段代码中可以看出,
task所在线程每次使用的threadlocal都是从captured,
和此时线程holder中的threadlocal没有关系。
如果没有关系,那为什么还要备份和恢复呢?这样不会有内存泄漏吗?

@yearyeardiff

  • 『和此时线程holder中的threadlocal没有关系』
    这个过程 和 『holder中的threadlocal』是有关的。具体请点进replay方法看其实现。
  • 不会『内存泄漏』。
    • 可以通过完整的实现逻辑 来分析确定
    • 可以看看这个issue的讨论: 是否有内存泄露的风险? 是否有内存泄露的风险? #96
    • 也做过 内存的压力测试。
      更多说明 参见 文档 - 性能测试,有脚本和测试运行的说明,你可以自己也试试。

另外,推荐看一下 Issue

@oldratlee oldratlee self-assigned this Mar 26, 2019
@oldratlee oldratlee added the ❓question Further information is requested label Mar 26, 2019
@yearyeardiff
Copy link
Author

yearyeardiff commented Mar 27, 2019

懂了,之前的理解是因为没有考虑到下面的场景。

new Thread( () -> {
    // 在new线程中,执行线程池操作
    final TtlRunnable task = TtlRunnable.get(() -> System.out.println(ttl.get()));
    executorService.submit(task);
}).start();

解答很详细,感谢!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
❓question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants