zoukankan      html  css  js  c++  java
  • ThreadLocal的基本原理与实现

    一、概念

      首先,ThreadLocal并不是一个Thread,这个类提供了线程局部变量,这些变量不同于它们的普通对应物,因为访问某个变量的每个线程都有自己的局部变量,它独立于变量的初始化副本。

    二、基本原理

      ThreadLocal是如何做到为每一线程维护变量的副本的呢?下面通过源码(jdk1.7版本)来阐述ThreadLocal的基本原理。

      在具体分析之前,先做几点说明:

      1:在TreadLocal中有一个静态内部类ThreadLocalMap

    static class ThreadLocalMap 
    {
        static class Entry extends WeakReference<ThreadLocal> 
        {
                /** The value associated with this ThreadLocal. */
                Object value;
                Entry(ThreadLocal k, Object v)
         {
                    super(k);
                    value = v;
           }
         }   
        ...
    }

      2:ThreadLocal中又定义一个键值对Entry,它用ThreadLocal作为键值。我们看到在Thread类中有一个ThreadLocalMap的类型的变量叫做threadLocals。

      

      下面具体分析一下ThreadLocal的两个关键函数get()和set():

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

      在get函数中,首先获取到当前的线程t,再根据t获取ThreadLocalMap。下面试getMap()函数:

    ThreadLocalMap getMap(Thread t) 
    {
    return t.threadLocals; }

      可以看到,该函数返回就是我们上面提到的每个线程都有的ThreadLocalMap类型变量threadLocals。

      如果map不为空,则根据map.getEntry(this)获取Entry键值对。注意:这里的this指的是当前的ThreadLocal对象,一个Thread可能对应不止一个ThreadLocal,想要知道具体是Thread对应的哪个ThreadLocal,就要在Thread中维护一个ThreadLocalMap,以ThreadLocal为键,就可以找到Thread在某个ThreadLocal里对应的本地数据。获取到Entry后,我们就可以拿到保存在Entry里面的value值了。

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

      如果map为空,则调用setInitialValue()函数进行初始化。并返回initialValue函数返回的值,不覆写initialValue的情况下,返回的是null。

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

      2、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);
        }

      set函数同样是先获取ThreadLocalMap类型的变量map。

      如果map不为空,则:

     private void set(ThreadLocal key, Object value)
     {
                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();
            }

      如果map为空,则

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

    三、总结

      ThreadLocal是通过下面的方式来实现为每一个线程维护变量的副本的:

      在ThreadLocal类中定义了一个ThreadLocalMap,每一个Thread都有一个ThreadLocalMap类型的变量threadLocals,就是用threadLocals来存储每一个线程的变量副本,threadLocals内部有一个Entry数组,我们根据键值线程对象,来找到对应线程的变量副本。

  • 相关阅读:
    linux cat 文件操作
    linux 如何显示一个文件的某几行(中间几行)
    dvm进程,linux进程,应用程序进程是否同一概念
    android-----JNI中的log打印
    push代码到github时,每次都要输入用户名和密码的问题
    linux下ndk编译命令行程序及配置
    Ubuntu16.04安装openjdk-7-jdk
    JNI——访问数组
    转:MyBatis学习总结(Mybatis总结精华文章)
    转:mybatis3中@SelectProvider的使用技巧
  • 原文地址:https://www.cnblogs.com/xujian2014/p/5777849.html
Copyright © 2011-2022 走看看