什么时候应该使用ThreadLocal变量?

它是如何使用的?


当前回答

自Java 8发布以来,有更多的声明性方法来初始化ThreadLocal:

ThreadLocal<String> local = ThreadLocal.withInitial(() -> "init value");

在Java 8发布之前,你必须做以下事情:

ThreadLocal<String> local = new ThreadLocal<String>(){
    @Override
    protected String initialValue() {
        return "init value";
    }
};

此外,如果用于ThreadLocal的类的实例化方法(构造函数,工厂方法)不接受任何参数,您可以简单地使用方法引用(在Java 8中引入):

class NotThreadSafe {
    // no parameters
    public NotThreadSafe(){}
}
    
ThreadLocal<NotThreadSafe> container = ThreadLocal.withInitial(NotThreadSafe::new);

注意: 计算是惰性的,因为你传递的java.util.function.Supplier lambda只在调用ThreadLocal#get但value之前没有计算时才会计算。

其他回答

Threadlocal提供了一种非常简单的零成本实现对象可重用性的方法。

我遇到过这样一种情况,在每次更新通知时,多个线程都在创建可变缓存的映像。

我在每个线程上使用Threadlocal,然后每个线程只需要重置旧映像,然后在每次更新通知时从缓存中再次更新它。

来自对象池的通常可重用对象具有与之相关的线程安全成本,而此方法没有。

由于ThreadLocal是对给定线程中的数据的引用,因此在使用线程池的应用服务器中使用ThreadLocal时,可能会导致类加载泄漏。在使用ThreadLocal的remove()方法清理你get()或set()的任何ThreadLocals时,你需要非常小心。

如果你在完成时不清理,它持有的任何类的引用作为部署的webapp的一部分将保留在永久堆中,永远不会被垃圾收集。重新部署/取消部署webapp不会清除每个线程对你的webapp类的引用,因为线程不是你的webapp所拥有的。每次后续部署都将创建该类的新实例,该实例永远不会被垃圾收集。

由于java.lang.OutOfMemoryError: PermGen空间,你最终会出现内存不足的异常,在谷歌搜索之后可能只是增加-XX:MaxPermSize,而不是修复这个错误。

如果您最终遇到了这些问题,您可以通过使用Eclipse的Memory Analyzer和/或遵循Frank Kieviet的指南和后续内容来确定哪个线程和类保留了这些引用。

更新:重新发现Alex Vasseur的博客条目,它帮助我找到了一些我遇到的ThreadLocal问题。

从本质上讲,当您需要一个变量的值依赖于当前线程,并且不方便您以其他方式将值附加到线程(例如,子类化线程)。

典型的情况是,其他框架创建了运行代码的线程,例如servlet容器,或者使用ThreadLocal更有意义,因为你的变量“在它的逻辑位置”(而不是挂在thread子类或其他哈希映射中的变量)。

在我的网站上,我有一些关于何时使用ThreadLocal的进一步讨论和示例,您可能也会感兴趣。

有些人主张使用ThreadLocal作为一种方法,在某些并发算法中,当你需要线程号时,将“线程ID”附加到每个线程上(参见Herlihy & Shavit)。在这种情况下,检查你是否真的得到了好处!

Many frameworks use ThreadLocals to maintain some context related to the current thread. For example when the current transaction is stored in a ThreadLocal, you don't need to pass it as a parameter through every method call, in case someone down the stack needs access to it. Web applications might store information about the current request and session in a ThreadLocal, so that the application has easy access to them. With Guice you can use ThreadLocals when implementing custom scopes for the injected objects (Guice's default servlet scopes most probably use them as well).

ThreadLocals are one sort of global variables (although slightly less evil because they are restricted to one thread), so you should be careful when using them to avoid unwanted side-effects and memory leaks. Design your APIs so that the ThreadLocal values will always be automatically cleared when they are not needed anymore and that incorrect use of the API won't be possible (for example like this). ThreadLocals can be used to make the code cleaner, and in some rare cases they are the only way to make something work (my current project had two such cases; they are documented here under "Static Fields and Global Variables").

在多线程代码中使用类似SimpleDateFormat的类助手有三种场景,其中最好的一种是使用ThreadLocal

场景

1-通过锁或同步机制使用类似的共享对象,使应用程序变慢

线程池操作场景

2-在方法中作为局部对象使用

在线程池中,在这个场景中,如果我们有4个线程每个线程有1000个任务时间,那么我们有 创建了4000 SimpleDateFormat对象,并等待GC删除它们

3-使用ThreadLocal

在线程池中,如果我们有4个线程,我们给每个线程一个SimpleDateFormat实例 我们有4个线程,4个SimpleDateFormat对象。

不需要锁机制和对象的创建和销毁。(良好的时间复杂度和空间复杂度)

https://www.youtube.com/watch?v=sjMe9aecW_A