ThreadLocal叫线程本地变量,他为每个线程都创建了一个副本变量,每个线程都只能访问自己的副本变量。
作用:让线程自己独立保存一份自己的变量副本(多线程情况下变量安全的一种解决思路)
使用场景:当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候。
每个线程都有一个ThreadLocalMap的实例对象,并且通过ThreadLocal管理ThreadLocalMap。
public T get() {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
//this是当前的ThreadLocalMap(key),getEntry通过key拿到value
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
内存泄漏问题:
首先先注意ThreadLocal 使用弱引用是为了预防内存泄漏的。 ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用,弱引用的特点是,如果这个对象只存在弱引用,那么在下一次垃圾回收的时候必然会被清理掉。假设我们创建一个ThreadLocal 对象:ThreadLocal tl = new ThreadLocal ();并调用set方法。我们先来看一下set方法:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
可以发现,map.set(this, value);map的key是this,而this就是ThreadLocal对象。那假设现在我们不想使用这个tl对象了,把它设置为null。但map中的key始终指向着这个tl对象,也就发生了内存泄漏。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
所以如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候会被清理掉的,这样一来 ThreadLocalMap中使用这个 ThreadLocal 的 key 也会被清理掉。但是,value 是强引用,不会被清理,这样一来就会出现 key 为 null 的 value。
ThreadLocalMap实现中已经考虑了这种情况,在调用 set()、get()、remove() 方法的时候,会清理掉 key 为 null 的记录。如果说会出现内存泄漏,那只有在出现了 key 为 null 的记录后,没有手动调用 remove() 方法,并且之后也不再调用 get()、set()、remove() 方法的情况下。
使用示例:
public class ThreadLocalTest {
public static void main(String[] args) {
final ThreadLocal<String> threadLocal1 = new ThreadLocal<>();
final ThreadLocal<Integer> threadLocal2 = new ThreadLocal<>();
new Thread(new Runnable() {
@Override
public void run() {
threadLocal1.set("A");
threadLocal2.set(1);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
System.out.println(threadLocal1.get());
System.out.println(threadLocal2.get());
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
threadLocal1.set("B");
threadLocal2.set(2);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
System.out.println(threadLocal1.get());
System.out.println(threadLocal2.get());
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
System.out.println(threadLocal1.get());
System.out.println(threadLocal2.get());
}
}).start();
}
}
输出为:
Thread-2
null
null
Thread-0
A
1
Thread-1
B
2
可以看到对数据存储后,只有在线程中才可以获取到存储的数据,对于其他线程来说是无法获取到数据。
网友评论