Java 何时以及如何使用ThreadLocal变量?
什么时候应该使用变量 它是如何使用的?说得很好:“访问[线程局部变量](通过其get或set方法)的每个线程都有自己的、独立初始化的变量副本”Java 何时以及如何使用ThreadLocal变量?,java,multithreading,concurrency,thread-local,thread-confinement,Java,Multithreading,Concurrency,Thread Local,Thread Confinement,什么时候应该使用变量 它是如何使用的?说得很好:“访问[线程局部变量](通过其get或set方法)的每个线程都有自己的、独立初始化的变量副本” 当每个线程都必须有自己的副本时,可以使用一个。默认情况下,数据在线程之间共享。在Java中,如果每个线程都有一个不同的数据,那么您可以选择将该数据传递给每个需要(或可能需要)它的方法,或者将该数据与线程相关联。如果所有方法都需要传递一个公共的“上下文”变量,那么到处传递数据可能是可行的 如果不是这样的话,您可能不想用额外的参数将方法签名弄得杂乱无章。在非
当每个线程都必须有自己的副本时,可以使用一个。默认情况下,数据在线程之间共享。在Java中,如果每个线程都有一个不同的数据,那么您可以选择将该数据传递给每个需要(或可能需要)它的方法,或者将该数据与线程相关联。如果所有方法都需要传递一个公共的“上下文”变量,那么到处传递数据可能是可行的 如果不是这样的话,您可能不想用额外的参数将方法签名弄得杂乱无章。在非线程世界中,您可以使用与全局变量相当的Java解决问题。在一个线程化的词中,全局变量的等价物是线程局部变量。一个可能(也是常见)的用法是,当您有一些不是线程安全的对象,但您希望避免访问该对象(我正在看您,)。相反,为每个线程提供它自己的对象实例 例如:
public class Foo
{
// SimpleDateFormat is not thread-safe, so give one to each thread
private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue()
{
return new SimpleDateFormat("yyyyMMdd HHmm");
}
};
public String formatIt(Date date)
{
return formatter.get().format(date);
}
}
公共类Foo
{
//SimpleDataFormat不是线程安全的,因此为每个线程提供一个
私有静态最终ThreadLocal格式化程序=新ThreadLocal(){
@凌驾
受保护的SimpleDataFormat初始值()
{
返回新的SimpleDataFormat(“yyyyMMdd HHmm”);
}
};
公共字符串格式(日期)
{
返回格式化程序.get().format(日期);
}
}
.由于
ThreadLocal
是对给定线程中数据的引用,因此在使用线程池的应用程序服务器中使用ThreadLocal
时,可能会导致类加载泄漏。在使用ThreadLocal
的remove()
方法清理get()
或set()
时,需要非常小心
如果完成后不进行清理,它对作为已部署webapp一部分加载的类的任何引用都将保留在中,并且永远不会被垃圾收集。重新部署/取消部署webapp不会清除每个线程对webapp类的引用,因为线程不属于webapp所有。每个后续部署将创建一个永远不会被垃圾收集的类的新实例
由于java.lang.OutOfMemoryError:PermGen space
的原因,最终会出现内存不足的异常,在谷歌搜索之后,可能只会增加-XX:MaxPermSize
,而不是修复错误
如果最终遇到这些问题,可以通过使用和/或遵循和来确定哪个线程和类保留这些引用
更新:重新发现它帮助我跟踪了我遇到的一些ThreadLocal
问题。基本上,当您需要一个变量的值依赖于当前线程时,不方便您以其他方式将该值附加到线程上(例如,子类化线程)
典型的情况是其他框架创建了代码运行的线程,例如servlet容器,或者使用ThreadLocal更有意义,因为此时变量处于“逻辑位置”(而不是挂在线程子类或其他哈希映射中的变量)
在我的网站上,我有一些可能也感兴趣的进一步信息
有些人主张使用ThreadLocal作为一种方式,在某些需要线程编号的并发算法中为每个线程附加一个“线程ID”(参见Herlihy&Shavit)。在这种情况下,检查你是否真的得到了好处 正如@unknown(google)所提到的,它的用法是定义一个全局变量,其中引用的值在每个线程中都是唯一的。它的用法通常需要存储与当前执行线程链接的某种上下文信息
我们在JavaEE环境中使用它将用户标识传递给不支持JavaEE的类(不能访问HttpSession或EJB SessionContext)。通过这种方式,代码可以从任何地方访问标识,而无需在每个方法调用中显式传递标识,它将标识用于基于安全性的操作
大多数Java EE调用中的请求/响应操作周期使这种类型的使用变得简单,因为它提供了定义良好的入口和出口点来设置和取消设置ThreadLocal。许多框架使用ThreadLocals来维护与当前线程相关的一些上下文。例如,当当前事务存储在ThreadLocal中时,不需要在每次方法调用中都将其作为参数传递,以防堆栈中的某个人需要访问它。Web应用程序可能将有关当前请求和会话的信息存储在ThreadLocal中,以便应用程序能够轻松访问它们。有了Guice,您可以在为注入的对象实现时使用ThreadLocals(Guice的默认设置很可能也使用它们)
ThreadLocals是一种全局变量(尽管由于它们仅限于一个线程,所以不那么糟糕),因此在使用它们时应小心,以避免不必要的副作用和内存泄漏。设计API,使ThreadLocal值在不再需要时始终自动清除,并且不可能错误使用API(例如)。ThreadLocals可用于使代码更干净,在某些罕见的情况下,它们是使某些东西工作的唯一方法(我当前的项目有两种这样的情况;它们记录在“静态字段和全局变量”下)。您必须非常小心使用ThreadLocal模式
// 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();
}
}
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()));
}
}
Key = One ThreadLocal object shared across threads.
value = Mutable object which has to be used synchronously, this will be instantiated for each thread.
class SimpleDateFormatInstancePerThread {
private static final ThreadLocal<SimpleDateFormat> dateFormatHolder = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd") {
UUID id = UUID.randomUUID();
@Override
public String toString() {
return id.toString();
};
};
System.out.println("Creating SimpleDateFormat instance " + dateFormat +" for Thread : " + Thread.currentThread().getName());
return dateFormat;
}
};
/*
* Every time there is a call for DateFormat, ThreadLocal will return calling
* Thread's copy of SimpleDateFormat
*/
public static DateFormat getDateFormatter() {
return dateFormatHolder.get();
}
public static void cleanup() {
dateFormatHolder.remove();
}
}
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();
}
}
private static ThreadLocal<Connection> connectionHolder
= new ThreadLocal<Connection>() {
public Connection initialValue() {
return DriverManager.getConnection(DB_URL);
}
};
public static Connection getConnection() {
return connectionHolder.get();
}
ThreadLocal<Cipher> local = ThreadLocal.withInitial(() -> "init value");
ThreadLocal<String> local = new ThreadLocal<String>(){
@Override
protected String initialValue() {
return "init value";
}
};
class NotThreadSafe {
// no parameters
public NotThreadSafe(){}
}
ThreadLocal<NotThreadSafe> container = ThreadLocal.withInitial(NotThreadSafe::new);
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();
}
}