ThreadLocal 线程本地变量,变量为线程独有,每个线程保存变量的副本,对副本的改动,对其他的线程而言是透明的。
特性
- The object is not shared between threads, so it can be used without the need for synchronization
- it is available throughout the life of the thread, meaning you don’t have to pass it all over the place through method calls
适用场景
- 每个线程都需要自己的数据存储对象
- 实例被多个方法共享,但是不希望多线程共享
- 空间换时间的场景
一个典型的例子就是用 ThreadLocal 来保存一次请求的 Session 数据,程序的不同地方可能需要读取 Session 的内容,也要往 Session 中写入数据。
construct an instance
Create instance with new:
ThreadLocal<Integer> threadLocalValue = new ThreadLocal<>();
Or using the withInitial()
static method:
ThreadLocal<Integer> threadLocal = Threadlocal.withInitial(() -> 1);
常用方法
// 设置当前线程的线程局部变量的值
void set(Object value);
// 该方法返回当前线程所对应的线程局部变量
public Object get();
// 将当前线程局部变量的值删除
public void remove();
ThreadLocal 类允许我们创建只能被同一个线程读写的变量,通常的用法是当有一些 Object 不是线程安全,但是又想避免使用同步 访问机制时。比如 SimpleDateFormat,因此可以使用 ThreadLocal 来给每一个线程提供一个线程自己的对象。
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("yyyy-MM-dd HH:mm:ss");
}
};
public String formatIt(Date date)
{
return formatter.get().format(date);
}
}
ThreadLocal 对象是给定线程中对象的引用,因此在服务端使用线程池时极有可能造成 classloading leaks 内存泄露 ,在使用时需要特别注意清理。ThreadLocal 持有的任何对象空间在 Java 永久堆上,即使重新部署 webapp 也不回收这部分内存,可能造成 java.lang.OutOfMemoryError 异常。
原理
利用 HashMap,在 map 中保存了 Thread 名 -> 线程变量的关系,因此在多线程之间是隔离的,但同时也耗费了内存空间。