ThreadLocal理解

来源:互联网 发布:世界著名ufo事件 知乎 编辑:程序博客网 时间:2024/06/08 09:39

首先举例说明ThreadLocal类的使用:

public class ThreadTest {    static ThreadLocal<String> tLocal = new ThreadLocal<String>();    public static void main(String[] args) {        // TODO Auto-generated method stub        try {            tLocal.set(Thread.currentThread().getName());//将main线程名称保存到tlocal中            //使用匿名类创建一个新线程            Thread thread = new Thread(){                public void run(){                    tLocal.set(Thread.currentThread().getName());                    System.out.println(tLocal.get());                }            };            thread.start();            thread.join();            System.out.println(tLocal.get());        } catch (InterruptedException e) {            // TODO: handle exception            e.printStackTrace();        }           }}

运行结果:

Thread-0Main

根据运行结果可知,在threadlocal中为每个线程存储一个单独的值。下面,结合上面例子,分析源码具体了解该类的原理:

首先threadlocal实例化,查看构造函数,没有任何其它操作。

public ThreadLocal() {}

Set()方法理解:

public void set(T value) {        //获取当前线程        Thread t = Thread.currentThread();        //ThreadLocalMap是ThreadLocal的静态内部类,具体见下面getMap方法,        //第一次执行该方法,map是null,        ThreadLocalMap map = getMap(t);        if (map != null)            //具体见下面set方法            map.set(this, value);        else            //当map为null是,创建map,具体见下面的createMap源码            createMap(t, value);}//在Thread类中,定义了threadLocals变量ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocalMap.set()方法:

private void set(ThreadLocal key, Object value) {            //获取类型为Entry的table数组,Entry是ThreadLocalMap的内部类            Entry[] tab = table;            int len = tab.length;            //根据key的hash值与15做&运算,得出下标            int i = key.threadLocalHashCode & (len-1);            //循环,当e不为null,执行循环体            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;                }            }            //实例化Entry,赋给tab[i],其实threadlocal本质就是将每个线程中的            //theadlocal.threadlocalmap对象和共享的变量,形成entry对象,            tab[i] = new Entry(key, value);            int sz = ++size;            //当table数组中的元素大于10,扩展table;即每个线程中的threadlocal           //数量大于10时,会对table数组扩展            if (!cleanSomeSlots(i, sz) && sz >= threshold)                rehash(); }

ThreadLocal.createMap(Thread t,T firstValue)方法:

//为每个线程创建ThreadLocalMap实例,void createMap(Thread t, T firstValue) {        t.threadLocals = new ThreadLocalMap(this, firstValue); }//ThreadLocalMap构造函数,传入ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {        //创建Entry类型的数组,长度为16        table = new Entry[INITIAL_CAPACITY];        //获取存放Entry实例的下标        int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);        table[i] = new Entry(firstKey, firstValue);        size = 1;        //设置扩展table长度的阈值        setThreshold(INITIAL_CAPACITY); }

以上分析代码可知,ThreadLocal在多线程中的应用中的原理如下图:
threadlocal原理图
根据图可知,每个线程中,都有自己独立的了ThreadLocalMap类的实例,ThreadLocalMap中的key是tLocal对象的引用,value表示每个线程中的独立的变量值,ThreadLocal的set方法的实质就是:若当前线程中ThreadLocalMap为null,则实例化ThreadLocalMap,同时传入ThreadLocal的引用和变量值;若不为null,执行ThreadLocalMap的set方法,同样需要传入ThreadLocal的引用和变量值。

当需要获取变量值时,调用ThreadLocal的get方法,自动获取当前线程中的ThreadLocalMap对象,并获取相应的变量值。

ThreadLocal并不是什么线程,确切地说是一个线程的变量,通过该变量,可以实现多线程之间的安全访问全局变量。ThreadLocal同synchronized,都是解决线程安全问题,ThreadLocal是通过“空间换取时间”的方式,synchronized是通过“时间换取空间”的方式。

原创粉丝点击