ThreadLocal类源码解析

来源:互联网 发布:疯狂联盟座狼升级数据 编辑:程序博客网 时间:2024/05/18 02:25

一、什么是ThreadLocal

  该类提供了线程局部 (thread-local) 变量。这种变量在多线程环境下访问(通过get或set方法访问)时能保证各个线程里的变量相对独立于其他线程内的变量。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。

例如,以下类生成对每个线程唯一的局部标识符。线程 ID 是在第一次调用 UniqueThreadIdGenerator.getCurrentThreadId() 时分配的,在后续调用中不会更改。

import java.util.concurrent.atomic.AtomicInteger; public class UniqueThreadIdGenerator {     private static final AtomicInteger uniqueId = new AtomicInteger(0);     private static final ThreadLocal < Integer > uniqueNum =          new ThreadLocal < Integer > () {             @Override protected Integer initialValue() {                 return uniqueId.getAndIncrement();         }     };     public static int getCurrentThreadId() {         return uniqueId.get();     } } // UniqueThreadIdGenerator

   每个线程都保持对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。

二、ThreadLocal 的实现原理

  每个ThreadLocal类创建一个Map,然后用线程的ID作为Map的key,实例对象作为Map的value,这样就能达到各个线程的值隔离的效果。为了实现该设计思想,在ThreadLocal类有以下方法

方法名 描述 initialValue() 返回此线程局部变量的当前线程的“初始值”。 set(T value) 将此线程局部变量的当前线程副本中的值设置为指定值。 get() 返回此线程局部变量的当前线程副本中的值 remove() 移除此线程局部变量当前线程的值。

  
  下面我们实现原理的源码解析:

    protected T initialValue() {        return null;    }

   该方法返回此线程局部变量的当前线程的“初始值”。线程第一次使用 get() 方法访问变量时将调用此方法,但如果线程之前调用了 set(T) 方法,则不会对该线程再调用 initialValue 方法。通常,此方法对每个线程最多调用一次,但如果在调用 get() 后又调用了 remove(),则可能再次调用此方法。
该实现返回 null;如果程序员希望线程局部变量具有 null 以外的值,则必须为 ThreadLocal 创建子类,并重写此方法。通常将使用匿名内部类完成此操作。

    /**     * 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)                return (T)e.value;        }        return setInitialValue();    }

   该方法返回此线程局部变量的当前线程副本中的值。如果变量没有用于当前线程的值,则先将其初始化为调用 initialValue() 方法返回的值。

    /**     * 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) {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);    }

   该方法将此线程局部变量的当前线程副本中的值设置为指定值。大部分子类不需要重写此方法,它们只依靠 initialValue() 方法来设置线程局部变量的值。

    /**     * 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     * <tt>initialValue</tt> method in the current thread.     *     * @since 1.5     */     public void remove() {         ThreadLocalMap m = getMap(Thread.currentThread());         if (m != null)             m.remove(this);     }

 该方法主要用于移除此线程局部变量当前线程的值。

 在上面的方法中我们注意到其使用了一个ThreadLocalMap 类 ,其是如何设计的呢?

 在ThreadLocal 中提供了一个静态内部类 ThreadLocalMap。每个Thread维护一个ThreadLocalMap映射表,这个映射表的key是ThreadLocal实例本身,value是真正需要存储的Object。当Thread销毁之后对应的ThreadLocalMap也就随之销毁了,能减少内存使用量。
观察源码可以看到如下:

 static class ThreadLocalMap {  //ThreadLocal类中的getMap 和creatMap方法   ThreadLocalMap getMap(Thread t) {        return t.threadLocals;    }    /**     * Create the map associated with a ThreadLocal. Overridden in     * InheritableThreadLocal.     *     * @param t the current thread     * @param firstValue value for the initial entry of the map     * @param map the map to store.     */    void createMap(Thread t, T firstValue) {        t.threadLocals = new ThreadLocalMap(this, firstValue);    } }
0 0
原创粉丝点击