Fork me on GitHub

Java并发包(ThreadLocal )第一节

一、ThreadLocal能做什么?

  1. "线程本地变量"或"线程局部变量"
  2. 作用域为当前线程,而不是某个具体任务。
  3. 声明周期和线程的声明周期相同(JDK实现中比线程的生命周期更短,减少了内存泄漏的可能)。
  4. 线程与任务剥离,从而达到线程封闭的目的。

二、存在的问题

  线程死亡之后,任务对象可能仍然存在(这才是最普遍的情况),从而ThreadLocal对象仍然存在。我们不能要求线程在死亡之前主动删除其使用的ThreadLocal对象,所以valueMap中该线程对应的entry()无法回收。

  这种实现“将线程相关的域封闭于任务对象,而不是线程中”。所以ThreadLocal的实现中最重要的一点就是——将线程相关的域封闭在当前线程实例中,虽然域仍然在任务对象中声明、set和get,却与任务对象无关。

三、ThreadLocal源码分析

  1. void set(T value)方法

源码:
/**
 * Sets the current thread's copy of this thread-local variable
 * to the specified value.  Most subclasses will have no need to
 * overridethis method, relying solely on the {@link#initialValue}
 * method to set the values of thread-locals.
 *
 * @paramvalue 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. 通过当前线程实例获取到ThreadLocalMap对象
    ThreadLocalMap map = getMap(t);
    if (map != null)
        //3. 如果Map不为null,则以当前threadLocl实例为key,值为value进行存入
        map.set(this, value);
    else
        //4.map为null,则新建ThreadLocalMap并存入value
        createMap(t, value);
}
  数据value是真正的存放在了ThreadLocalMap这个容器中了,并且是以当前threadLocal实例为key
获取ThreadLocalMap
//******
//ThreadLocal values pertaining to this thread. This map is maintained
 /* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*******
 
 
/**
 * Get the map associated with a ThreadLocal. Overridden in
 * InheritableThreadLocal.
 *
 * @param  t the current thread
 * @returnthe map
 */
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}
  设置value时,要根据当前线程t获取一个ThreadLocalMap类型的map,真正的value保存在这个map中。这验证了之前的一部分想法——ThreadLocal变量保存在一个“线程相关”的map中。
也就是说ThreadLocalMap的引用是作为Thread的一个成员变量,被Thread进行维护的。回过头再来看看set方法,当map为Null的时候会通过createMap(t,value)方法:
/**
 * Create the map associated with a ThreadLocal. Overridden in
 * InheritableThreadLocal.
 *
 * @paramt the current thread
 * @paramfirstValue value for the initial entry of the map
 */
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}
  该方法就是new一个ThreadLocalMap实例对象,然后同样以当前threadLocal实例作为key,值为value存放到threadLocalMap中,然后将当前线程对象的threadLocals赋值为threadLocalMap
set方法总结:
  通过当前线程对象thread获取该thread所维护的threadLocalMap,
    若threadLocalMap不为null,则以threadLocal实例为key,值为value的键值对存入threadLocalMap,
    若threadLocalMap为null的话,就新建threadLocalMap然后在以threadLocal为键,值为value的键值对存入即可。
  1. T 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.
 *
 * @returnthe current thread's value of this thread-local
 */
public T get() {
    //1. 获取当前线程的实例对象
    Thread t = Thread.currentThread();
    //2. 获取当前线程的threadLocalMap
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        //3. 获取map中当前threadLocal实例为key的值的entry
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            //4. 当前entitiy不为null的话,就返回相应的值value
            T result = (T)e.value;
            return result;
        }
    }
    //5. 若map为null或者entry为null的话通过该方法初始化,并返回该方法返回的value
    return setInitialValue();
}
 
/**
 * Variant of set() to establish initialValue. Used instead
 * of set() in case user has overridden the set() method.
 *
 * @returnthe initial value
 */
private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}
 
protected T initialValue() {
    return null;
}
  这个方法是protected修饰的也就是说继承ThreadLocal的子类可重写该方法,实现赋值为其他的初始值
关于get方法来总结一下:
  通过当前线程thread实例获取到它所维护的threadLocalMap,然后以当前threadLocal实例为key获取该map中的键值对(Entry),若Entry不为null则返回Entry的value。如果获取threadLocalMap为null或者Entry为null的话,就以当前threadLocal为Key,value为null存入map后,并返回null。
  1. remove()
/**
 * Removes the current thread's value for this thread-local
 * variable.  If this thread-local variable is subsequently
 * {@linkplain#get read} by the current thread, its value will be
 * reinitialized by invoking its {@link#initialValue} method,
 * unless its value is {@linkplain#set set} by the current thread
 * in the interim.  This may result in multiple invocations of the
 * {@codeinitialValue} method in the current thread.
 *
 * @since1.5
 */
 public void remove() {
    //1. 获取当前线程的threadLocalMap
     ThreadLocalMap m = getMap(Thread.currentThread());
     if (m != null)
        //2. 从map中删除以当前threadLocal实例为key的键值对
         m.remove(this);
 }
  删除数据当然是从map中删除数据,先获取与当前线程相关联的threadLocalMap然后从map中删除该threadLocal实例为key的键值对即可

参考链接:

  1. https://juejin.im/post/59db31c16fb9a00a4843dc36
  1. https://www.javazhiyin.com/18063.html
  1. https://www.javazhiyin.com/18065.html
  1. https://www.javazhiyin.com/18072.html #
  1. 《java高并发程序设计》
posted @ 2020-06-20 22:11  Geek仁杰  阅读(204)  评论(0编辑  收藏  举报