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

它是如何使用的?


当前回答

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

其他回答

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").

由于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问题。

正如@unknown(谷歌)所提到的,它的用途是定义一个全局变量,其中引用的值在每个线程中都是唯一的。它的使用通常需要存储某种链接到当前执行线程的上下文信息。

我们在Java EE环境中使用它来将用户标识传递给不支持Java EE的类(不能访问HttpSession或EJB SessionContext)。通过这种方式,代码可以从任何地方访问标识,而不必在每个方法调用中显式地传递它。

大多数Java EE调用中的操作的请求/响应周期使得这种类型的使用很容易,因为它提供了定义良好的入口和出口点来设置和取消设置ThreadLocal。

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。最常用但不是线程安全的对象之一是数据库Connection和JMSConnection。

如何?

例如,Spring框架通过将这些连接对象保存在ThreadLocal变量中,在幕后大量使用ThreadLocal来管理事务。在高层,当事务启动时,它获得连接(并禁用自动提交)并将其保存在ThreadLocal中。在进一步的db调用中,它使用相同的连接与db通信。最后,它从ThreadLocal获取连接,提交(或回滚)事务并释放连接。

我认为log4j也使用ThreadLocal来维护MDC。