java 1.8 ThreadLocal源码分析

参考资料:
http://www.cnblogs.com/dolphin0520/p/3920407.html
http://blog.csdn.net/lufeng20/article/details/24314381
http://blog.csdn.net/devilkin64/article/details/7916630

1. 关于ThreadLocal

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。

创建线程局部私有的变量,避免多线程之间的竞争,是一种简单的多线程安全类。

        我对ThreadLocal的理解就是:某个数据是共享的,但是共享的粒度是线程内部,即该线程里运行的所有代码,所有方法内获取到的都是同一个值。
区别与其他的形式:如果不用ThreadLocal,而是使用static 变量,创建出的全局共享的话,在多线程的环境下,需要同步(简单的例子是多线程下的双判定的单例模式),如数据库连接管理类ConnectionManager,这个例子见下:

class ConnectionManager {

    private static Connection connect = null;

    public synchronized static Connection openConnection() {
        if(connect == null){
            connect = DriverManager.getConnection();
        }
        return connect;
    }

    public synchronized  static void closeConnection() {
        if(connect!=null)
            connect.close();
    }
}

        一个简单的同步是直接使用synchronized 方法,但是这种的实现使得每次只能有一个线程在操作ConnectionManager.但是这种情况下,并发度太小(就这个情景下,使用连接池性能更好,这里只是使用这个说明做个ThreadLocal引入的例子),变成每个线程一个connectionmanager,这样的话,就不用在connectionmanager类中使用同步方法了,增加了处理时间。
        那ThreadLocal和直接的将某个对象变成普通的局部变量,有什么不同呢?
如下面的代码:

class ConnectionManager {

    private  Connection connect = null;

    public Connection openConnection() {
        if(connect == null){
            connect = DriverManager.getConnection();
        }
        return connect;
    }

    public void closeConnection() {
        if(connect!=null)
            connect.close();
    }
}

        我对次的理解是这样的,加入有个管理类Manager,里面有个成员是普通的成员变量SomeType obj,这样,每个Manager实例都有一个obj对象,这和上面需求的每个Thread一份obj还是不同的。而要做到每个Thread一份obj,要么将obj对象放到Thread子类或相关的类中(如Runnable等),或是,每创建一个Thread,手动的绑定一个obj对象,不然,是没有办法做到每个Thread一份obj。但是这样的话,使得一些独立的功能和Thread耦合起来了,或是使得你需要提前的需要知道Thread个数,好让你手动的创建obj对象,独立的绑定上去。一般来说,我们更希望一些工具类,管理类能够独立出去,而不是和Thread,Runanble杂在一起,能够自动的感知有多少的Thread,就创建多少的拷贝,这就是使用ThreadLocal的好处!!

2. 使用例子

class Manager{
    static ThreadLocal< String> s=new ThreadLocal<String>();;
}
public class Test implements Runnable{

        public static void main(String args[]) 
        {
            Test r1=new Test();         
            Thread s1=new Thread(r1);      
            Thread s2=new Thread(r1);       

            s1.start();
            s2.start();
        }
        @Override
        public void run() {
            if(Manager.s.get()==null)
                Manager.s.set(new String(Thread.currentThread().getName()));
            System.out.println(Manager.s.get());

            //use it do something else
        }

}

3. 方法分析

a.构造函数

    public ThreadLocal() {  }

b.get()

 public T get() 
    {
        Thread t = Thread.currentThread();
        //获取与当前线程绑定的ThreadLocalMap对象
        //getMap(t) ->  return t.threadLocals;
        ThreadLocalMap map = getMap(t);
        if (map != null) 
        {
            //在hashmap中查询以当前ThreadLocal为key对象的Entry
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        //因为ThreadLocaMap是懒加载的,即存在一个有效的条目时,才会创建出
        //与当前Thread相关的threadLocalMap对象,所以,在set调用前,调用了
        //get,会得到null的threadLocalMap对象
        return setInitialValue();
    }

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

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

         Thread对象中成员变量:ThreadLocal.ThreadLocalMap threadLocals = null;

关于ThreadLocal.ThreadLocalMap类:简而言之,就是一个map,里面的每个条目Entry的key为WeakReference类型的对象,value为该变量的该线程中的实际值(即如果如果该线程中的ThreadLocal对象没有其他对象引用了的话,该Entry就会被回收)

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?> > {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
  //ThreadLocalMap是一个线性探测hash表,默认的初始容量为16,threshold=2/3,ThreadLocal为key,当添加entry时,使用ThreadLocal的hashcode来在线性表中查找合适的位置
           private Entry getEntry(ThreadLocal<?> key) 
        {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }

set方法

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

//threadlocalmap的set方法
        private void set(ThreadLocal<?> key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) 
            {
                ThreadLocal<?> k = e.get();

                if (k == key) 
                {
                    e.value = value;
                    return;
                }

                if (k == null) 
                {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

4. 总结

  1. ThreadLocal有一个final类型的int变量(int threadLocalHashCode = nextHashCode()),表示该TreadLocal变量的hash值,该hash值有一个全局的AtomicInteger来管理,即系统中ThreadLocal变量有同一个AtomicInteger变量自增而得,因为Thread数母随机,基本能保证同一个Thread中的ThreadLocal变量hashcode不断变化,使得能均匀的映射到ThreadLocalMap上。
  2. 每个线程Thread对象都有一个ThreadLocal.ThreadLocalMap对象,线程的ThreadLocal变量存放在该ThreadLocalMap中。
  3. ThreadLocal为懒加载的,即当使用到第一个ThreadLocal对象时,才会创建出线程的ThreadLocalMap对象,该map为线程探测线性map,初始的默认大小为16,threshold为2/3.
  4. ThreadLocalMap使用循环数组存放兼值对ThreadLocalMap.Entry为WeakSoftReference子类,即当系统中不存在对ThreadLocal对象时,该Entry就会被回收(由于没有创建该WeakSoftReference对象相关的 reference queue,所以,只保证在内存不充足的时候该Entry被回收)
  5. 在ThreadLocalMap中查找时,首先使用ThreadLocal对象(key)的hashcode定位可能数据索引,然后依次遍历,直到找到对应的Entry,或是遇到null的Entry
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值