什么时候应该使用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的用法。

其他回答

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

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

在我的网站上,我有一些关于何时使用ThreadLocal的进一步讨论和示例,您可能也会感兴趣。

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

第一个用例——每个线程上下文,提供线程安全性和性能 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的主要原因

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

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

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

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

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

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