1.什么是ThreadLocal?
ThreadLocal是Java为Thread提供的本地存储机制,可以在Thread通过ThreadLocal(set方法)缓存数据,缓存之后可以在线程执行时的任意执行点、任意时间点获取(get方法)缓存的数据。
Thread的ThreadLocal本地存储内存区相互隔离,并不共享;但是如果ThreadLocal关联的缓存数据为堆对象时,不同的Thread的ThreadLocal关联到同一个堆对象,这个时候如果有修改操作不是线程安全的。
2.ThreadLocal实现的原理
Thread内部有一个类型为ThreadLocalMap的threadLocals字段,用于存储线程的ThreadLocal数据。每个Thread线程对象都存在一个ThreadLocalMap。
ThreadLocalMap本质上是一个哈希表,采用开放地址法实现,在使用过程中会出现Entry数组扩缩容。
ThreadLocalMap是ThreadLocal的内部类,不能直接使用,只能使用ThreadLocal的api方法。
public class ThreadLocal<T> {
public T get() {
#获取当前线程Thread的ThreadLocal
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
// 其他操作
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
}
3.使用ThreadLocal的注意事项
static class Entry extends WeakReference<ThreadLocal<?>>
ThreadLocalMap的Entry为弱引用,方便JVM能自动回收ThreadLocal对象,在方法执行完成后,当ThreadLocal对象不存在强引用时,JVM在接下来出现的第一次gc时会自动回收ThreadLocal对象。
虽然通过弱引用Entry.k指向的ThreadLocal对象能自动回收,但是Entry.value指向的数据对象并不会自动回收,当Entry.k为null时,后边如果调用了这个ThreadLocal的get,set方法时会触发移除Entry对象。
Thread保持执行时,Entry对象被Thread强引用,Entry.value也会强引用数据对象,也就是说只要Entry对象不从ThreadLocalMap中移除,Entry对象和Entry.valude指向的数据对象就会被Thread强引用,都是到根可达的,就不会被JVM回收。
Thread执行退出后相关的ThreadLocalMap,ThreadLocal和数据对象都会被JVM回收;线程池中的线程生命周期同JVM一样,ThreadLocal使用不当会导致内存泄露。
当ThreadLocal缓存数据不在使用时一定要使用ThreadLocal.remove从ThreadLocalMap中移除,让JVM能回收这些内存。
4.ThreadLocal的使用场景
经典的使用场景是连接管理,一个线程持有一个连接,不需要通过参数传递就能在不同的方法中使用这个连接,不同的线程使用不同的连接,不需要加锁同步,提高并发性能。