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

它是如何使用的?


当前回答

第一个用例——每个线程上下文,提供线程安全性和性能 SpringFramework类中的实时示例-

LocaleContextHolder TransactionContextHolder RequestContextHolder DateTimeContextHolder

第二个用例——当我们不想在线程之间共享一些东西,同时由于性能成本而不想使用同步/锁定时 SimpleDateFormat为日期创建自定义格式

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author - GreenLearner(https://www.youtube.com/c/greenlearner)
 */
public class ThreadLocalDemo1 {
    SimpleDateFormat sdf = new SimpleDateFormat("dd-mm-yyyy");//not thread safe
    ThreadLocal<SimpleDateFormat> tdl1 = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-dd-mm"));

    public static void main(String[] args) {
        ThreadLocalDemo1 d1 = new ThreadLocalDemo1();

        ExecutorService es = Executors.newFixedThreadPool(10);

        for(int i=0; i<100; i++) {
            es.submit(() -> System.out.println(d1.getDate(new Date())));
        }
        es.shutdown();
    }

    String getDate(Date date){

//        String s = tsdf.get().format(date);
        String s1 = tdl1.get().format(date);
        return s1;
    }
}

使用技巧

尽可能使用局部变量。这样我们就可以避免使用ThreadLocal 尽可能地将功能委托给框架 如果使用ThreadLocal并将状态设置在其中,请确保在使用后清理它,否则它可能成为OutOfMemoryError的主要原因

其他回答

试试这个小例子,感受一下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'。

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

线程局部变量通常用于防止基于线程的设计中的共享 可变单例或全局变量。

在不使用连接池的情况下,它可以用于为每个线程建立单独的JDBC连接等场景。

private static ThreadLocal<Connection> connectionHolder
           = new ThreadLocal<Connection>() {
      public Connection initialValue() {
           return DriverManager.getConnection(DB_URL);
          }
     };

public static Connection getConnection() {
      return connectionHolder.get();
} 

当您调用getConnection时,它将返回与该线程关联的连接。对于其他属性(如dateformat、事务上下文等),您不希望在线程之间共享,也可以这样做。

您也可以使用局部变量来实现相同的目的,但是这些资源通常会占用创建时间,因此您不希望在使用它们执行某些业务逻辑时一次又一次地创建它们。然而,ThreadLocal值存储在线程对象本身中,一旦线程被垃圾收集,这些值也就消失了。

这个链接很好地解释了ThreadLocal的用法。

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

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

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