什么时候应该使用ThreadLocal变量?
它是如何使用的?
什么时候应该使用ThreadLocal变量?
它是如何使用的?
当前回答
在Java中,如果您有一个每个线程都可以变化的数据,那么您可以选择将该数据传递给每个需要(或可能需要)它的方法,或者将该数据与线程关联。如果你的所有方法都需要传递一个公共的“上下文”变量,那么到处传递数据可能是可行的。
如果不是这样,您可能不希望用额外的参数来打乱方法签名。在非线程环境中,可以使用Java中等价的全局变量来解决这个问题。在线程词中,与全局变量等价的是线程局部变量。
其他回答
可以使用threadlocal变量的两个用例- 1-当我们需要将状态与线程关联时(例如,用户ID或事务ID)。这通常发生在web应用程序中,每个发送到servlet的请求都有一个与之关联的唯一transactionID。
// This class will provide a thread local variable which
// will provide a unique ID for each thread
class ThreadId {
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId =
ThreadLocal.<Integer>withInitial(()-> {return nextId.getAndIncrement();});
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
}
注意,这里的withInitial方法是使用lambda表达式实现的。 2-另一个用例是当我们想要一个线程安全的实例时,我们不想使用同步,因为同步的性能成本更高。其中一种情况是使用SimpleDateFormat。由于SimpleDateFormat不是线程安全的,所以我们必须提供机制使其线程安全。
public class ThreadLocalDemo1 implements Runnable {
// threadlocal variable is created
private static final ThreadLocal<SimpleDateFormat> dateFormat = new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue(){
System.out.println("Initializing SimpleDateFormat for - " + Thread.currentThread().getName() );
return new SimpleDateFormat("dd/MM/yyyy");
}
};
public static void main(String[] args) {
ThreadLocalDemo1 td = new ThreadLocalDemo1();
// Two threads are created
Thread t1 = new Thread(td, "Thread-1");
Thread t2 = new Thread(td, "Thread-2");
t1.start();
t2.start();
}
@Override
public void run() {
System.out.println("Thread run execution started for " + Thread.currentThread().getName());
System.out.println("Date formatter pattern is " + dateFormat.get().toPattern());
System.out.println("Formatted date is " + dateFormat.get().format(new Date()));
}
}
Java中的ThreadLocal类使您能够创建只能由同一个线程读写的变量。因此,即使两个线程正在执行相同的代码,并且该代码有一个对ThreadLocal变量的引用,那么两个线程也不能看到彼此的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").
从本质上讲,当您需要一个变量的值依赖于当前线程,并且不方便您以其他方式将值附加到线程(例如,子类化线程)。
典型的情况是,其他框架创建了运行代码的线程,例如servlet容器,或者使用ThreadLocal更有意义,因为你的变量“在它的逻辑位置”(而不是挂在thread子类或其他哈希映射中的变量)。
在我的网站上,我有一些关于何时使用ThreadLocal的进一步讨论和示例,您可能也会感兴趣。
有些人主张使用ThreadLocal作为一种方法,在某些并发算法中,当你需要线程号时,将“线程ID”附加到每个线程上(参见Herlihy & Shavit)。在这种情况下,检查你是否真的得到了好处!
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();
}
}