目录
一、ThreadLocal原理
1、每个Thread对象都含有一个ThreadLocalMap,ThreadLocalMap是由Entry对象构成,Entry继承自weakReference<ThreadLocal<?>>,一个Entry由ThreadLocal对象和Object构成。由此可见,Entry的key是ThreadLocal对象,并且是一个弱引用,当没指向key的强引用后,该key就会被垃圾回收器回收;
2、当执行set方法时,ThreadLocal首先会获取当前线程对象,然后获取当前线程的ThreadLocalMap对象,再以当前ThreadLocal对象为key,将值存储进ThreadLocaoMap对象中;
3、当执行get方法时,ThreadLocal首先会获取当前线程对象,然后获取当前线程的ThreadLocalMap对象,再以当前ThreadLocal对象为key,获取对应的value。
二、TheadLocal特性
1、ThreadLocal是Java中所提供的线程本地缓存机制,可以利用该机制将数据缓存在某个线程内部,该线程可以在任意时刻、任意方法中获取缓存的数据;(由于每一条线程均含有各自私有的ThreadLocalMap容器,这些容器互相独立互不影响,因此不会存在线程安全性问题,从而也无需使用同步机制来保证多条线程访问容器的互斥性)
2、ThreadLocal底层是通过ThreadLocalMap来实现的,每个Thread对象中都存在一个ThreadLocalMap,Map的key为ThreadLocal对象,Map的value为需要缓存的值;
3、如果在线程池中使用ThreadLocal会造成内存泄漏,因为ThreadLocal对象使用完后,应该要把设置的key和value也就是Entry对象进行回收,但线程池中不会回收,而线程对象是通过强引用指向ThreadLocalMap,ThreadLocalMap也是通过强引用指向Entry对象,线程不被回收,Entry对象也就不会被回收,从而出现内存泄漏。解决办法:在使用了ThreadLocal对象之后,手动调用ThreadLocal的remove方法,手动清除Entry对象。
二、TheadLocal使用场景
1、在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束;
2、线程间数据隔离;
3、进行事务操作,用于存储线程事务信息;
4、数据库连接,Session会话管理。
注:Spring框架z在事务开始时会给当前线程绑定一个Jdbc Connection,在整个事务过程都是使用该线程绑定的Connection来执行数据库操作,实现了事务的隔离性,Springk框架里面就是用的ThreadLocal来实现这种隔离。
案例:
public class Person {
private ThreadLocal<String> name = new ThreadLocal<String>();
public String getName() {
return this.name.get();
}
public void setName(String name) {
this.name.set(name);
}
}
class Demo{
public static void main(String[] args) {
Person person = new Person();
new Thread(new Runnable() {
@Override
public void run() {
person.setName("张三");
try {
Thread.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("测试1:" + person.getName());
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
person.setName("李四");
try {
Thread.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("测试2:" + person.getName());
}
}).start();
}
}