什么时候应该使用ThreadLocal变量?
它是如何使用的?
什么时候应该使用ThreadLocal变量?
它是如何使用的?
当前回答
什么时候?
当一个对象不是线程安全的时候,为了避免影响可伸缩性的同步,给每个线程一个对象,并保持它的线程作用域,即ThreadLocal。最常用但不是线程安全的对象之一是数据库Connection和JMSConnection。
如何?
例如,Spring框架通过将这些连接对象保存在ThreadLocal变量中,在幕后大量使用ThreadLocal来管理事务。在高层,当事务启动时,它获得连接(并禁用自动提交)并将其保存在ThreadLocal中。在进一步的db调用中,它使用相同的连接与db通信。最后,它从ThreadLocal获取连接,提交(或回滚)事务并释放连接。
我认为log4j也使用ThreadLocal来维护MDC。
其他回答
从本质上讲,当您需要一个变量的值依赖于当前线程,并且不方便您以其他方式将值附加到线程(例如,子类化线程)。
典型的情况是,其他框架创建了运行代码的线程,例如servlet容器,或者使用ThreadLocal更有意义,因为你的变量“在它的逻辑位置”(而不是挂在thread子类或其他哈希映射中的变量)。
在我的网站上,我有一些关于何时使用ThreadLocal的进一步讨论和示例,您可能也会感兴趣。
有些人主张使用ThreadLocal作为一种方法,在某些并发算法中,当你需要线程号时,将“线程ID”附加到每个线程上(参见Herlihy & Shavit)。在这种情况下,检查你是否真的得到了好处!
[参考]ThreadLocal无法解决共享对象的更新问题。建议使用staticThreadLocal对象,该对象由同一线程中的所有操作共享。 [必选]remove()方法必须由ThreadLocal变量实现,特别是在使用经常重用线程的线程池时。否则,可能会影响后续的业务逻辑,并导致意想不到的问题,如内存泄漏。
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();
}
}
线程局部变量通常用于防止基于线程的设计中的共享 可变单例或全局变量。
在不使用连接池的情况下,它可以用于为每个线程建立单独的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的用法。
Java中的ThreadLocal类使您能够创建只能由同一个线程读写的变量。因此,即使两个线程正在执行相同的代码,并且该代码有一个对ThreadLocal变量的引用,那么两个线程也不能看到彼此的ThreadLocal变量。
阅读更多