ThreadLocal
是 Java 中的一个实用工具类,它提供了线程局部变量。
这些变量在每个线程中都有一个独立的副本,因此对这些变量的操作不会影响到其他线程中的相应副本。这对于避免多线程环境下的数据竞争和同步问题非常有用。
ThreadLocal的使用要点
- 在一个类中创建一个静态
ThreadLocal
类型的对象
- 向这个对象中
set
一个值或者对象。这里需要注意连接,set
方法最终是把对象存进了Thread
对象持有的一个Map
对象中了,Thread
对象就是当前线程对象。
这时候你应该有个疑问,如果创建一个线程创建两个ThreadLocal
对象,分别调用这两个对象的set方法,因为这两个对象在一个线程,所以会使用到同一个Thread
对象的Map
对象,调用set
方法时,又没指定key
,那么存入Map
时,key
是什么?会不会导致值的覆盖?
如果能搞清楚这个问题,基本上ThreadLocal
你已经掌握的差不多了。
肯定不会覆盖,因为两个ThreadLocal
对象调用set方法时,虽然使用了相同的Map
,且没有指定key,但在存入map时,把this
对象即ThreadLocal
对象作为key
,因为是两个ThreadLocal对象,所以key是不一样的。
- 在其他类中获取存入
ThreadLocal
对象中的值。
ThreadLocal的基本原理
-
存储机制:
-
ThreadLocal
类本身并不直接存储线程局部变量的值,而是通过Thread
类实现存储。 -
每个
Thread
对象都有一个ThreadLocalMap
用来存放线程局部变量。
-
当你在一个
ThreadLocal
变量上调用set()
方法时,会将当前线程与该值关联起来,并保存在当前线程的ThreadLocalMap
中。 -
当调用
get()
方法时,返回的是当前线程中与该ThreadLocal
关联的值。
-
-
内部实现:
- 每个
ThreadLocal
对象都有一个ThreadLocalMap
的引用,这个引用是通过ThreadLocal
的内部类ThreadLocalMap
实现的。 Thread
类有一个ThreadLocalMap
类型的成员变量threadLocals
,用于存储所有ThreadLocal
对象的键值对。- 当创建一个新的
ThreadLocal
并调用set()
方法时,会在这个ThreadLocalMap
中设置一个键值对,其中键是ThreadLocal
对象自身,值是要存储的数据。 ThreadLocalMap
使用了一个轻量级的哈希表来存储键值对。
- 每个
-
垃圾回收:
- 由于
ThreadLocal
的引用可能存在于ThreadLocalMap
内部,如果没有适当的处理,可能会导致内存泄漏。 ThreadLocal
提供了remove()
方法来清理不再需要的线程局部变量,从而帮助垃圾回收器进行回收。- 在
ThreadLocalMap
中,当一个ThreadLocal
对象被显式地设置为null
或者该对象没有更多的活动引用时,其对应的条目会被从ThreadLocalMap
中移除。
- 由于
使用示例
public class Example {
public static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
threadLocal.set(100);
System.out.println("Thread 1: " + threadLocal.get());
});
Thread t2 = new Thread(() -> {
threadLocal.set(200);
System.out.println("Thread 2: " + threadLocal.get());
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在这个例子中,两个线程分别设置了不同的值到同一个 ThreadLocal
变量,但每个线程只能看到自己设置的值。
以上就是 ThreadLocal
的基本原理和实现方式。