Java 何时以及如何使用 ThreadLocal 变量?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/817856/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 19:50:14  来源:igfitidea点击:

When and how should I use a ThreadLocal variable?

javamultithreadingconcurrencythread-localthread-confinement

提问by

When should I use a ThreadLocalvariable?

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

How is it used?

它是如何使用的?

采纳答案by overthink

One possible (and common) use is when you have some object that is not thread-safe, but you want to avoid synchronizingaccess to that object (I'm looking at you, SimpleDateFormat). Instead, give each thread its own instance of the object.

一种可能(且常见)的用途是当您有一些不是线程安全的对象,但您想避免同步访问该对象时(我正在看着您,SimpleDateFormat)。相反,给每个线程它自己的对象实例。

For example:

例如:

public class Foo
{
    // SimpleDateFormat is not thread-safe, so give one to each thread
    private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
        @Override
        protected SimpleDateFormat initialValue()
        {
            return new SimpleDateFormat("yyyyMMdd HHmm");
        }
    };

    public String formatIt(Date date)
    {
        return formatter.get().format(date);
    }
}

Documentation.

文档

回答by RichieHindle

The documentationsays it very well: "each thread that accesses [a thread-local variable] (via its get or set method) has its own, independently initialized copy of the variable".

文档说得很好:“每个访问[线程局部变量](通过其 get 或 set 方法)的线程都有自己的、独立初始化的变量副本”。

You use one when each thread must have its own copy of something. By default, data is shared between threads.

当每个线程必须拥有自己的某物副本时,您可以使用一个。默认情况下,数据在线程之间共享。

回答by user100464

In Java, if you have a datum that can vary per-thread, your choices are to pass that datum around to every method that needs (or may need) it, or to associate the datum with the thread. Passing the datum around everywhere may be workable if all your methods already need to pass around a common "context" variable.

在 Java 中,如果您有一个可以随线程变化的数据,您的选择是将该数据传递给需要(或可能需要)它的每个方法,或者将数据与线程相关联。如果您的所有方法都需要传递一个通用的“上下文”变量,则在任何地方传递数据可能是可行的。

If that's not the case, you may not want to clutter up your method signatures with an additional parameter. In a non-threaded world, you could solve the problem with the Java equivalent of a global variable. In a threaded word, the equivalent of a global variable is a thread-local variable.

如果情况并非如此,您可能不希望使用附加参数来弄乱您的方法签名。在非线程世界中,您可以使用 Java 等效的全局变量来解决问题。在线程化的词中,全局变量的等价物是线程局部变量。

回答by Phil M

Since a ThreadLocalis a reference to data within a given Thread, you can end up with classloading leaks when using ThreadLocals in application servers using thread pools. You need to be very careful about cleaning up any ThreadLocals you get()or set()by using the ThreadLocal's remove()method.

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

If you do not clean up when you're done, any references it holds to classes loaded as part of a deployed webapp will remain in the permanent heapand will never get garbage collected. Redeploying/undeploying the webapp will not clean up each Thread's reference to your webapp's class(es) since the Threadis not something owned by your webapp. Each successive deployment will create a new instance of the class which will never be garbage collected.

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

You will end up with out of memory exceptions due to java.lang.OutOfMemoryError: PermGen spaceand after some googling will probably just increase -XX:MaxPermSizeinstead of fixing the bug.

由于java.lang.OutOfMemoryError: PermGen space某些谷歌搜索可能只会增加-XX:MaxPermSize而不是修复错误,您最终会遇到内存不足的异常。

If you do end up experiencing these problems, you can determine which thread and class is retaining these references by using Eclipse's Memory Analyzerand/or by following Frank Kieviet's guideand followup.

如果你最终会遇到这些问题,您可以决定哪个线程和类保留通过使用这些引用Eclipse的内存分析器和/或按照弗兰克Kieviet的指导后续操作

Update: Re-discovered Alex Vasseur's blog entrythat helped me track down some ThreadLocalissues I was having.

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

回答by Neil Coffey

Essentially, when you need a variable's value to depend on the current threadand it isn't convenient for you to attach the value to the thread in some other way(for example, subclassing thread).

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

A typical case is where some other framework has created the threadthat your code is running in, e.g. a servlet container, or where it just makes more sense to use ThreadLocal because your variable is then "in its logical place" (rather than a variable hanging from a Thread subclass or in some other hash map).

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

On my web site, I have some further discussion and examples of when to use ThreadLocalthat may also be of interest.

在我的网站上,我有一些关于何时使用 ThreadLocal 的进一步讨论和示例,这些内容可能也很有趣。

Some people advocate using ThreadLocal as a way to attach a "thread ID" to each thread in certain concurrent algorithms where you need a thread number (see e.g. Herlihy & Shavit). In such cases, check that you're really getting a benefit!

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

回答by Robin

As was mentioned by @unknown (google), it's usage is to define a global variable in which the value referenced can be unique in each thread. It's usages typically entails storing some sort of contextual information that is linked to the current thread of execution.

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

We use it in a Java EE environment to pass user identity to classes that are not Java EE aware (don't have access to HttpSession, or the EJB SessionContext). This way the code, which makes usage of identity for security based operations, can access the identity from anywhere, without having to explicitly pass it in every method call.

我们在 Java EE 环境中使用它来将用户身份传递给不支持 Java EE 的类(无权访问 HttpSession 或 EJB SessionContext)。这样,将身份用于基于安全的操作的代码可以从任何地方访问身份,而无需在每个方法调用中显式传递它。

The request/response cycle of operations in most Java EE calls makes this type of usage easy since it gives well defined entry and exit points to set and unset the ThreadLocal.

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

回答by Esko Luontola

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 scopesfor the injected objects (Guice's default servlet scopesmost probably use them as well).

许多框架使用 ThreadLocals 来维护与当前线程相关的一些上下文。例如,当当前事务存储在 ThreadLocal 中时,您不需要通过每个方法调用将其作为参数传递,以防堆栈中的某个人需要访问它。Web 应用程序可能会将有关当前请求和会话的信息存储在 ThreadLocal 中,以便应用程序可以轻松访问它们。使用 Guice,您可以在为注入的对象实现自定义范围时使用 ThreadLocals (Guice 的默认servlet 范围很可能也使用它们)。

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 hereunder "Static Fields and Global Variables").

ThreadLocals 是一种全局变量(尽管由于它们仅限于一个线程而稍微不那么邪恶),因此在使用它们时应该小心以避免不必要的副作用和内存泄漏。设计您的 API,以便 ThreadLocal 值在不再需要时始终自动清除,并且不会错误使用 API(例如像这样)。ThreadLocals 可用于使代码更清晰,并且在极少数情况下,它们是使某些事情起作用的唯一方法(我当前的项目有两个这样的情况;它们在此处记录在“静态字段和全局变量”下)。

回答by Jeff Richley

You have to be very careful with the ThreadLocal pattern. There are some major down sides like Phil mentioned, but one that wasn't mentioned is to make sure that the code that sets up the ThreadLocal context isn't "re-entrant."

您必须非常小心使用 ThreadLocal 模式。Phil 提到了一些主要的缺点,但没有提到的一个是确保设置 ThreadLocal 上下文的代码不是“可重入的”。

Bad things can happen when the code that sets the information gets run a second or third time because information on your thread can start to mutate when you didn't expect it. So take care to make sure the ThreadLocal information hasn't been set before you set it again.

当设置信息的代码第二次或第三次运行时,可能会发生不好的事情,因为您的线程上的信息可能会在您出乎意料的时候开始发生变化。因此,在再次设置之前,请注意确保 ThreadLocal 信息尚未设置。

回答by Colselaw

Nothing really new here, but I discovered today that ThreadLocalis very useful when using Bean Validation in a web application. Validation messages are localized, but by default use Locale.getDefault(). You can configure the Validatorwith a different MessageInterpolator, but there's no way to specify the Localewhen 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 ThreadLocaland then have your custom MessageInterpolatorpick the Localefrom that. Next step is to write a ServletFilterwhich uses a session value or request.getLocale()to pick the locale and store it in your ThreadLocalreference.

这里没有什么新东西,但我今天发现这ThreadLocal在 Web 应用程序中使用 Bean 验证时非常有用。验证消息已本地化,但默认情况下使用Locale.getDefault(). 您可以Validator使用不同的配置MessageInterpolator,但无法指定Locale何时调用validate。所以,你可以创建一个静态ThreadLocal<Locale>(或更好,但与其他事物一般的容器,你可能需要ThreadLocal再有您的自定义MessageInterpolatorLocale的这一点。下一步是写一个ServletFilter,它使用一个会话值或request.getLocale()挑区域和存储它在您的ThreadLocal参考中。

回答by znlyj

Webapp server may keep a thread pool, and a ThreadLocalvar should be removed before response to the client, thus current thread may be reused by next request.

Webapp服务端可能会保留一个线程池,ThreadLocal在响应客户端之前需要移除一个var,这样当前线程可能会被下一次请求重用。