学习ThreadLocal实现原理
ThreadLocal的思想是以空间换时间,而锁的思想是以时间换空间,threadLocal的实现就是讲成员变量的副本拷贝到自己的线程内部,
因为不同的线程之间是隔离的,所以可以通过这种方式实现线程安全。
先来看一个示例:
/** * 分析ThreadLocal类的实现原理 * 我们解决线程安全问题,那么以时间换空间,使用锁机制。要么以空间 * 换时间,使用threadLocal,隔离线程 */ public class AnalysisThreadLocal { private static ThreadLocal<Map<String, String>> threadLocal = new ThreadLocal<Map<String, String>>() { @Override protected Map<String, String> initialValue() { return new HashMap<>(); } }; public static void main(String[] args) throws ParseException { new Thread(() -> { threadLocal.get().put("1", "aaaaa"); String key = threadLocal.get().get("1"); System.out.println(Thread.currentThread().getName() + " key: " + key); }).start(); new Thread(() -> { String key = threadLocal.get().get("1"); System.out.println(Thread.currentThread().getName() + " key: " + key); }).start(); } }
执行结果:
线程Thread-0 在自己的副本中put值,然后 在线程Thread-1获取不到。
get方法:
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
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();
}
看一下注释:这个方法会返回当前线程的thread-local变量副本,如果当前线程没有副本值,则首先
调用initialValue方法来初始化这个副本值。
首先到Thread类的成员变量上去取副本值,ThreadLocalMap map = getMap(t);
如果为null则调用初始化方法:
T value = initialValue();
这个方法会调到我们自己的实现类方法,实例化具体需要保证线程安全的变量。
然后将当前ThreadLocal对象与变量维护到ThreadLocalMap中,然后赋值给当前线程的字段ThreadLocals
再看一下ThreadLocalMap这个类,传进来的ThreadLocal对象与变量维护到Entry节点上,
完成以上设置,将ThreadLocal与变量封装成一个ThreadLocalMap,然后赋值给Thread的字段后,就把这个变量返回了。
然后我们可以使用这个变量,示例中我们的变量是一个HashMap,我们向里面put值。
然后在当前线程再get出来。
这时候getMap就可以拿到值,ThreadLocalMap
调用map.getEntry 拿到Entry值,然后在拿到变量的值HashMap,然后就可以根据key找到value值。
如果这时候另外一个线程也想拿到这个值,
然后这个线程为重复第一个线程的动作,也会调用初始化方法,拷贝一个副本,将ThreadLocal对象与副本封装成ThreadLocalMap,赋值给
当前线程的ThreadLocals字段,然后将变量返回,因为是新的HashMap,所以我们那第一个线程设置的key去取值,得到的一定是null。