一、ThreadLocal是什么
ThreadLocal为线程本地变量,早在JDK 1.2的版本中就提供java.lang.ThreadLocal,其特点为提供了线程内部存储能力,且变量只在本线程中使用,线程之间不共享。
二、ThreadLocal的使用
思考1
:
方法A调用方法B,方法B调用方法C,层层调用,直到调用方法Z,如何将变量X从方法A到方法Z中使用呢?
可以采用的方法有:
①变量X一层层传递,略显麻烦;
②将X设置为静态(static),但多线程同时读写共享资源X时,会产生数据不一致问题;
③将X放入ThreadLocal中,同一线程中,任一方法都可取用X;
思考2
:
观察下列代码,思考控制台能否打印出“hello A”。
public class ThreadLocalTest {
static ThreadLocal<String> tl = new ThreadLocal<>();
public static void main(String[] args) {
//线程A
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(tl.get());
}).start();
//线程B
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
tl.set("hello A");
}).start();
}
}
结论:控制台打印null,线程A无法get到内容。ThreadLocal为线程本地变量,线程之间彼此隔离。
三、ThreadLocal的实现原理
set方法源码:
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
//1.获取当前线程
Thread t = Thread.currentThread();
//2.将当前线程对象作为key值,传入 getMap() 方法中,最后返回ThreadLocalMap
ThreadLocalMap map = getMap(t);
//3.如果map不为null,则以ThreadLocal对象为key,放入ThreadLocal的对象为value,放入到map中
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
接下来我们看第2步,getMap() 方法具体是什么
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
接收到线程后,返回的是线程的成员变量!且类型是ThreadLocalMap。
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap中是这样描述的:
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
……
}
可以看到ThreadLocalMap的key(也就是ThreadLocal)是弱引用,会被GC当做垃圾回收掉。回收后ThreadLocalMap的key是null,而value还在,生命周期跟Thread一样长,如果没有手动删除对应key会造成
内存泄露
。
解决该问题的办法:使用完ThreadLocal后,调用remove方法。
remove方法源码:
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
文章写到这里,基本了解了ThreadLocal的使用,以及为什么能做到存储的变量可以线程之间彼此隔离。本文为自己的学习心得,有不足之处或者需要补充的地方欢迎在评论区交流,大家互相学习,共同进步。