什么时候应该使用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问题。

其他回答

在Java中,如果您有一个每个线程都可以变化的数据,那么您可以选择将该数据传递给每个需要(或可能需要)它的方法,或者将该数据与线程关联。如果你的所有方法都需要传递一个公共的“上下文”变量,那么到处传递数据可能是可行的。

如果不是这样,您可能不希望用额外的参数来打乱方法签名。在非线程环境中,可以使用Java中等价的全局变量来解决这个问题。在线程词中,与全局变量等价的是线程局部变量。

Nothing really new here, but I discovered today that ThreadLocal is very useful when using Bean Validation in a web application. Validation messages are localized, but by default use Locale.getDefault(). You can configure the Validator with a different MessageInterpolator, but there's no way to specify the Locale when you call validate. So you could create a static ThreadLocal<Locale> (or better yet, a general container with other things you might need to be ThreadLocal and then have your custom MessageInterpolator pick the Locale from that. Next step is to write a ServletFilter which uses a session value or request.getLocale() to pick the locale and store it in your ThreadLocal reference.

缓存,有时你必须计算相同的值很多时间,所以通过存储最后一组输入到一个方法和结果,你可以加快代码。通过使用线程本地存储,您可以避免考虑锁定问题。

ThreadLocal will ensure accessing the mutable object by the multiple threads in the non synchronized method is synchronized, means making the mutable object to be immutable within the method. This is achieved by giving new instance of mutable object for each thread try accessing it. So It is local copy to the each thread. This is some hack on making instance variable in a method to be accessed like a local variable. As you aware method local variable is only available to the thread, one difference is; method local variables will not available to the thread once method execution is over where as mutable object shared with threadlocal will be available across multiple methods till we clean it up.

通过定义:

Java中的ThreadLocal类允许您创建这样的变量 只能在同一线程上读写。这样,即使是两个线程 正在执行相同的代码,并且该代码有一个对 变量ThreadLocal,那么两个线程不能看到彼此的线程 ThreadLocal变量。

java中的每个线程都包含ThreadLocalMap。 在哪里

Key = One ThreadLocal object shared across threads.
value = Mutable object which has to be used synchronously, this will be instantiated for each thread.

实现ThreadLocal:

现在为ThreadLocal创建一个包装器类,它将保存如下所示的可变对象(有或没有initialValue())。现在这个包装器的getter和setter将工作于threadlocal实例,而不是可变对象。

如果threadlocal的getter()在线程的threadlocalmap中没有找到任何值;然后它将调用initialValue()来获得它相对于线程的私有副本。

class SimpleDateFormatInstancePerThread {

    private static final ThreadLocal<SimpleDateFormat> dateFormatHolder = new ThreadLocal<SimpleDateFormat>() {

        @Override
        protected SimpleDateFormat initialValue() {
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd") {
                UUID id = UUID.randomUUID();
                @Override
                public String toString() {
                    return id.toString();
                };
            };
            System.out.println("Creating SimpleDateFormat instance " + dateFormat +" for Thread : " + Thread.currentThread().getName());
            return dateFormat;
        }
    };

    /*
     * Every time there is a call for DateFormat, ThreadLocal will return calling
     * Thread's copy of SimpleDateFormat
     */
    public static DateFormat getDateFormatter() {
        return dateFormatHolder.get();
    }

    public static void cleanup() {
        dateFormatHolder.remove();
    }
}

现在wrapper.getDateFormatter()将调用threadlocal.get()并检查currentThread。threadLocalMap包含这个(threadlocal)实例。 如果是,返回对应threadlocal实例的值(SimpleDateFormat) 否则使用这个threadlocal实例initialValue()添加映射。

在此可变类上实现线程安全;每个线程都使用自己的可变实例,但使用相同的ThreadLocal实例。意味着所有线程将共享相同的ThreadLocal实例作为key,但不同的SimpleDateFormat实例作为value。

https://github.com/skanagavelu/yt.tech/blob/master/src/ThreadLocalTest.java

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