ThreadLocal简介ThreadLocal线程本地变量。为每个线程创建一个变量的副本,每个线程都只能访问自己的副本变量,实现了线程间的数据隔离。这样一来,就不会存在线程安全的问题
每个线程都可以通过ThreadLocal.get()和ThreadLocal.set()对本地变量进行操作而不会对其它线程的变量产生影响。
以下代码是TheadLocal的使用:
public static void main(String[] args) {
ThreadLocal<String> tl = new ThreadLocal<>();
Random random = new Random();
IntStream.range(0, 5).forEach(value -> new Thread(() -> {
tl.set(random.nextInt(10) + "");
System.out.println(Thread.currentThread() + "的值为: " + tl.get());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start());
}
// Thread[Thread-0,5,main]的值为: 4
// Thread[Thread-3,5,main]的值为: 9
// Thread[Thread-2,5,main]的值为: 7
// Thread[Thread-1,5,main]的值为: 4
// Thread[Thread-4,5,main]的值为: 8
从运行的结构中也可以看出,每个线程都有各自的值。
Thread类的内部维护着一个ThreadLocalMap的变量。ThreadLocalMap是ThreadLocal的静态内部类,在TheadLocalMap中定义了静态内部类Entry来进行数据存储,这是继承了弱引用。在ThreadLocalMap中,使用ThreadLocal作为key。
public class Thread implements Runnable {
ThreadLocal.ThreadLocalMap threadLocals = null;
}
public class ThreadLocal<T> {
private Entry[] table;
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
}
}
在调用ThreadLocal.set(T value)方法的时候,实际上调用的是ThreadLocalMap.set(ThreadLocal> key, Object value),调用TheadLocal.get()的时候,实际上调用的是ThreadLocalMap.getEntry(ThreadLocal> key)。ThreadLocal本身是不存储值的,它只是作为一个key,让线程从ThreadLocalMap中获取value。以下是相关代码:
public class ThreadLocal<T> {
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
static class ThreadLocalMap {
private Entry[] table;
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
}
}
ThreadLocal内存泄漏ThreadLocalMap中的key,也就是ThreadLocal,是弱引用,当ThreadLocal == null的时候,就要被垃圾回收器回收了。但是,ThreadLocalMap和Thread的生命周期是一致的,它不会被回收。这个时候ThreadLocalMap中就会出现key为null的value,就会存在这样一条引用链:Thread Ref -> Thread -> ThreadLocalMap -> Entry -> value永远无法回收,这样就造成了内存泄漏。
其实,在ThreadLocalMap的设计中已经考虑到了这些情况,也加上了相应的防护措施,在ThreadLocalMap.get/ThreadLocalMap.set/ThreadLocalMap.remove的时候,会清除掉ThreadLocalMap中key为null的value。
但是这些被动的预防措施并不能保证不会内存泄漏:
static的ThreadLocal,延长了ThreadLocal的生命周期,可能导致的内存泄漏;ThreadLocal后,执行ThreadLocal.remove()操作;ThreadLocal和synchronizedThreadLocal与Synchronized关键字都用于处理多线程并发访问变量的问题,不过两者处理问题的角度和思路不,前者是空间换取时间,后者是时间换取空间。
ThreadLocal
面试官:知道ThreadLocal嘛?谈谈你对它的理解?(基于jdk1.8)
面试官:ThreadLocal 不是啥高级的东西