什么时候应该使用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").

其他回答

Webapp服务器可能会保留一个线程池,并且在响应客户端之前应该删除ThreadLocal变量,因此当前线程可能会被下一个请求重用。

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

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

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

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

当设置信息的代码第二次或第三次运行时,可能会发生糟糕的事情,因为线程上的信息可能会在您没有预料到的时候开始发生变化。因此,在再次设置ThreadLocal信息之前,请注意确保没有设置它。

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是JVM专门提供的功能,仅为线程提供隔离的存储空间。与实例作用域变量的值一样,变量只能绑定到类的给定实例。每个对象都有其唯一的值,它们不能看到彼此的值。ThreadLocal变量的概念也是如此,在对象实例的意义上,它们是线程的本地线程,除了创建它的线程之外,其他线程看不到它。在这里看到的

import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;


public class ThreadId {
private static final AtomicInteger nextId = new AtomicInteger(1000);

// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId = ThreadLocal.withInitial(() -> nextId.getAndIncrement());


// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
    return threadId.get();
}

public static void main(String[] args) {

    new Thread(() -> IntStream.range(1, 3).forEach(i -> {
        System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
    })).start();

    new Thread(() -> IntStream.range(1, 3).forEach(i -> {
        System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
    })).start();

    new Thread(() -> IntStream.range(1, 3).forEach(i -> {
        System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
    })).start();

}
}