ThreadLocal源码笔记

        最近用到了ThreadLocal,因此看了一下JDK的源码。ThreadLocal是以空间换时间的典型,由于它避免了使用synchronized关键字来对变量加锁,从而节约了不少时间。ThreadLocal是为每一个线程建立一个变量的副本,因此每一个线程均可以访问这个副本的内容并修改,彼此之间的副本不会相互影响,所以看似会修改同一个变量,但实际上线程间修改的都是线程本地变量。例以下面这个数据库链接类:web

public class DatabaseUtil {
    private static final ThreadLocal<Connection> CON_HOLDER = new ThreadLocal<Connection>();

    public static Connection getConn() {
        Connection con = CON_HOLDER.get();
        if(null == con) {
            try {
                con = DriverManager.getConnection(url, username, pwd);
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                CON_HOLDER.set(con);
            }
        }
        return con;
    }

    // 其它函数
    ......
}

       咱们能够在Servlet中调用getConn()方法去获取一个链接,而后执行相应的数据库操做(暂且忽略这种作法的不规范性,先讨论多线程访问的问题)。Servlet在Java web中是单实例多线程的执行模式,这也是为何咱们通常不在Servlet类中定义成员变量的缘由,就是为了不线程不安全。那么当多个请求到来时,每一个请求都是在一个线程里面来访问servlet的方法的,所以看上去每一个线程获取到的链接都是同一个实例(getConn方法相似单例模式)。但实际上并非这样,每一个请求都会在它的线程中获取到不一样的Connection链接,这就是ThreadLocal的做用。首先看ThreadLocal的get()方法的源码:数据库

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();
}

      首先它获取了当前正在运行的线程,getMap()方法其实就是获取Thread中的ThreadLocalMap对象,并无什么特别的。而后从ThreadLocalMap中根据当前ThreadLocal对象做为key去获取对应的值,接着返回。Thread类中有如下两个成员值得注意:安全

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

/*
 * InheritableThreadLocal values pertaining to this thread. This map is
 * maintained by the InheritableThreadLocal class.
 */
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

     刚才的getMap()方法其实返回的就是threadLocal对象,如今Thread、ThreadLocal和ThreadLocalMap对象混在一块儿有点乱,下面来理清楚它们的关系:多线程

     1. 每一个Thread都有一个ThreadLocalMap对象threadLocals做为它的成员变量,它的key是ThreadLocal对象,值就是ThreadLocal中保存的值,若是一个线程访问了多个ThreadLocal对象,则ThreadLocalMap中就会有多个映射关系函数

     2. 每个ThreadLocal对象都有本身惟一的hash值,这个值就是用于在ThreadLocalMap中查找对应的value的,因此以ThreadLocal对象为key就能够找到对应的线程本地的值this

     3. ThreadLocalMap是ThreadLocal的一个静态内部类(就把它想象成一个普通的类就行了,只不过访问它须要经过ThreadLocal来访问),保存了不少个entry,每一个entry都是一个ThreadLocal与它的值的映射关系url

      说了这么多,仍是贴一张图比较直观:线程

    从上图能够看到,每一个线程都拥有一个ThreadLocalMap对象,它会保存ThreadLocal和value的映射关系,ThreadLocal就至关于一个中间人,每次线程方法它的get或set方法时,它都会首先获取当前运行的线程,而后获取线程中的ThreadLocalMap对象,因为每一个ThreadLocal对象都有惟一hash值,因此以ThreadLocal为key能够在当前线程中找到它对应的值。每一个线程都维护本身的ThreadLocalMap对象,这个就是真正的“副本”,经过ThreadLocal这个中介就能够保证成员变量的线程安全。每一个线程都有本身的映射(即ThreadLocalMap),当线程第一次设置ThreadLocal的值的时候,其实就是在本身的map中添加一条访问的ThreadLocal对象和值的映射,建立副本,而后就能够根据该ThreadLocal对象(也就是它的hashcode)找到对应的值,而不须要访问共享的变量。code

    ThreadLocal没有使用synchronized关键字,而是在Thread中维护一份本地的副本,若是每一个线程都但愿独享本身的成员变量时,ThreadLocal是一个好选择,但若是确实须要多线程共享一个成员变量时,那么仍是须要使用synchronized关键字来加锁保证线程安全的。对象