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

它是如何使用的?


当前回答

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

其他回答

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

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

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

试试这个小例子,感受一下ThreadLocal变量:

public class Book implements Runnable {
    private static final ThreadLocal<List<String>> WORDS = ThreadLocal.withInitial(ArrayList::new);

    private final String bookName; // It is also the thread's name
    private final List<String> words;


    public Book(String bookName, List<String> words) {
        this.bookName = bookName;
        this.words = Collections.unmodifiableList(words);
    }

    public void run() {
        WORDS.get().addAll(words);
        System.out.printf("Result %s: '%s'.%n", bookName, String.join(", ", WORDS.get()));
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(new Book("BookA", Arrays.asList("wordA1", "wordA2", "wordA3")));
        Thread t2 = new Thread(new Book("BookB", Arrays.asList("wordB1", "wordB2")));
        t1.start();
        t2.start();
    }
}

控制台输出,如果线程BookA先执行: 结果BookA: 'wordA1, wordA2, wordA3'。 结果BookB: 'wordB1, wordB2'。 控制台输出,如果先执行线程BookB: 结果BookB: 'wordB1, wordB2'。 结果BookA: 'wordA1, wordA2, wordA3'。

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

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

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