什么时候应该使用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的remove()方法清理你get()或set()的任何ThreadLocals时,你需要非常小心。

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

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

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

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

ThreadLocal in Java had been introduced on JDK 1.2 but was later generified in JDK 1.5 to introduce type safety on ThreadLocal variable. ThreadLocal can be associated with Thread scope, all the code which is executed by Thread has access to ThreadLocal variables but two thread can not see each others ThreadLocal variable. Each thread holds an exclusive copy of ThreadLocal variable which becomes eligible to Garbage collection after thread finished or died, normally or due to any Exception, Given those ThreadLocal variable doesn't have any other live references. ThreadLocal variables in Java are generally private static fields in Classes and maintain its state inside Thread.

阅读更多:Java示例程序和教程中的ThreadLocal

Java中的ThreadLocal类使您能够创建只能由同一个线程读写的变量。因此,即使两个线程正在执行相同的代码,并且该代码有一个对ThreadLocal变量的引用,那么两个线程也不能看到彼此的ThreadLocal变量。

阅读更多

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

场景

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

线程池操作场景

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

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

3-使用ThreadLocal

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

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

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

[参考]ThreadLocal无法解决共享对象的更新问题。建议使用staticThreadLocal对象,该对象由同一线程中的所有操作共享。 [必选]remove()方法必须由ThreadLocal变量实现,特别是在使用经常重用线程的线程池时。否则,可能会影响后续的业务逻辑,并导致意想不到的问题,如内存泄漏。