java.lang.ThreadLocal

package java.lang;
import java.lang.ref.*;

/**
 *
 * ThreadLocal是一个key,与一个值对应,它在每个线程中只有一个,但是在每一个不同的线程中都有一个拷贝
 * 这样可以保证线程安全,每个线程对象都有两个对象ThreadLocalMap:
 * threadLocals,inheritableThreadLocals,ThreadLocal作为key在Map中查找对应值
 * ThreadLocal对象一般都为类的静态属性
 *
 * 例子:
 *
class tt extends ThreadLocal {
 private static int num = 0;
 
 public Object initialValue() {
  return new Integer(num++);
    }
}

class kk {
 private static ThreadLocal tl = new tt();

 public int get() {
  return ((Integer) tl.get()).intValue();
 }
}
 * comment by liqiang
 *
 * @author  Josh Bloch and Doug Lea
 *
 */
public class ThreadLocal {
    /**
     *
     * 用于计算hashcode的方法
     *
     */
    private final int threadLocalHashCode = nextHashCode();

    /**
     * 存储下一个hashCode,在调用nextHashCode时改变
     */
    private static int nextHashCode = 0;

    //HashCode值的增量
    private static final int HASH_INCREMENT = 0x61c88647;

    /**
     *
     * 用与计算hashCode值的方法
     *
     */
    private static synchronized int nextHashCode() {
        int h = nextHashCode;
        //简单的做递增
        nextHashCode = h + HASH_INCREMENT;
        return h;
    }

    /**
     *
     * 此方法应被子类继承,当第一次调用get方法时会用此方法生成初始对象
     *
     * ThreadLodal对象所含对象的初始创建方法
     */
    protected Object initialValue() {
        return null;
    }

    /**
     *
     * 获得此ThreadLocal对应的值,如果线程的ThreadLocalMap
     * 没有创建则创建ThreadLocalMap对象,并调用此对象的initialValue
     * 方法获得初始对象,必将此值作为第一组值添加到Map中
     *
     */
    public Object get() {
     //取得当前线程
        Thread t = Thread.currentThread();
        //取得线程的ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
       
        //如果map不空,表示已被初始化过,则通过当前ThreadLodal对象
        //来获得所含对象,这里ThreadLodal对象相当是键值
        if (map != null)
            return map.get(this);

        //如果是第一次,则创建包含对象
        Object value = initialValue();
        //生成新Map,并将对象加入
        createMap(t, value);
        return value;
    }

    /**
     *
     * 设置此ThreadLocal对应的值
     *
     */
    public void set(Object value) {
     //获取当前线程对象
        Thread t = Thread.currentThread();
        //获取此线程兑现的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        if (map != null) //如果已经创建则查找,并设置
            map.set(this, value);
        else //如果没有创建,则创建当前线程的ThreadLocalMap对象,并将值对作为初始值加入
            createMap(t, value);
    }

    /*
     * Removes the value for this ThreadLocal.

     THIS METHOD COULD BE UNCOMMENTED TO ADD IT TO THE PUBLIC API IF THAT
     IS THOUGHT TO DESIRABLE AT SOME POINT.

     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

     */

    //取得线程的ThreadLocalMap对象
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

    /**
     *
     * 通过当前ThreadLocal对象和值组成初始值对创建当前线程的
     * ThreadLocalMap
     * @param t 当前线程对象
     * @param firstValue 初始Entry的值
     */
    void createMap(Thread t, Object firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

    /**
     *
     * 通过ThreadLocalMap中的指和其每个ThreadLocal对象上的childValue方法
     * 创建新的ThreadLocalMap
     *
     */
    static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }

    /**
     *
     * 由父ThreadLocalMap创建子ThreadLocalMap时计算新值的方法
     * 默认不支持,需要子类实现
     *
     */
    Object childValue(Object parentValue) {
        throw new UnsupportedOperationException();
    }

    /**
     * 一个内部类,表示操作ThreadLocal的HashMap
     * 注意HashMap的数据结构实现方式是通过一个数组,数组
     * 的元素是包含key很valu的entry对象
     */
    static class ThreadLocalMap {

        /**
         *
         * HashMap数据结构中的节点,如果entry.get() == null表示key不再有效
         * 则它对应的entry为陈腐的,将被清除掉
         *
         */
        private static class Entry extends WeakReference {
            //表示值
            private Object value;
           
            //k为key,v为值,而Reference包装的是key
            private Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }

        //Map中的数组的初始化大小
        private static final int INITIAL_CAPACITY = 16;

        //存放元素的数组,没有原始是一个Entry对象,包含key和value
        private Entry[] table;

        //数组中拥有的entry元素的个数,初始化为0
        private int size = 0;

        //发生扩展Map的边界值,抖动数
        private int threshold;

        //设置抖动数
        private void setThreshold(int len) {
            threshold = len * 2 / 3;
        }

        //取下一个位置,如果超过模leng则使用0位置,实现循环方式
        private static int nextIndex(int i, int len) {
            return ((i + 1 < len) ? i + 1 : 0);
        }

        //取得上一个位置,如果小于0,则使用数组的最后一个位置,实现循环方式
        private static int prevIndex(int i, int len) {
            return ((i - 1 >= 0) ? i - 1 : len - 1);
        }

        //通过key和value构建一个新ThreadLocalMap
        ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
            //创建一个数组
         table = new Entry[INITIAL_CAPACITY];
         //计算位标值
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            //生成一个新entry并赋给数组相应位置
            table[i] = new Entry(firstKey, firstValue);
            //设置长度为1
            size = 1;
            //设置抖动数
            setThreshold(INITIAL_CAPACITY);
        }

        //通过一个ThreadLocalMap对象创建对象
        private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];
     
            for (int j = 0; j < len; j++) {//迭代原对象数组中的每个元素
                Entry e = parentTable[j];
                if (e != null) {//相应数组元素不为空
                    //取得键
                 Object k = e.get();
                    if (k != null) {//键不为null
                     //转型
                        ThreadLocal key = (ThreadLocal)(k);
                        //通过父ThreadLocal的值和父ThreadLocal的childValue方法
                        //得到新的childValue值
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                       
                        //取得位标,注意方法是先与模按位与,如果此处有值,则通过nextIndex方法取下一个
                        int h = key.threadLocalHashCode & (len - 1); 
                        while (table[h] != null)
                            h = nextIndex(h, len);
                       
                        //将新值存放到相应位置
                        table[h] = c;
                        //长度增1
                        size++;
                    }
                }
            }
        }

        /**
         *
         * 通过key取得Map中的值
         *
         */
        private Object get(ThreadLocal key) {
         //计算出位置,通过按位与操作,i不会超过table.length - 1
            int i = key.threadLocalHashCode & (table.length - 1);
            //取得此位置的元素
            Entry e = table[i];
            //如果此元素不空,且所含key与查找key相等,则
            //表示找到对应值,直接返回
            if (e != null && e.get() == key)
                return e.value;

            //从位置i向下查找
            return getAfterMiss(key, i, e);
        }

        /**
         *
         * 如果通过key直接的hash操作没有得到对象,则通过此方法继续查找
         *
         * @param  key ThreadLocal对象,作为key
         * @param  i 从i开始查找
         * @param  e i位置的Entry
         *
         */
        private Object getAfterMiss(ThreadLocal key, int i, Entry e) {
            Entry[] tab = table;
            int len = tab.length;

            while (e != null) {//循环直到空元素
             //取得键
                Object k = e.get();
                if (k == key) //如果查找到则返回Entry的值
                    return e.value;
                if (k == null) //是陈腐的,则继续向下查找,找到则读取,并交换,找不到则创建
                    return replaceStaleEntry(key, null, i, true);

                //向下取得下一个Entry
                i = nextIndex(i, len);
                e = tab[i];
            }

            //如果没有查找到,则通过ThreadLocal的initialValue方法创建初始对象
            Object value = key.initialValue();
            //通过当前ThreadLocal对象,和新创建的值,生成Enrty对象
            //并发到数组中
            tab[i] = new Entry(key, value);
            //如果Entry数大于等于抖动数则清除陈腐Entry,扩大数组并重置元素
            if (++size >= threshold)
                rehash();

            //返回查找或生成的值
            return value;
        }

        /**
         *
         * 通过key设置value,没有则创建
         *
         */
        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)]) {
                Object k = e.get();

                //key匹配,找到此元素则设置其值
                if (k == key) {
                    e.value = value;
                    return;
                }

                //是陈腐的,则继续向下查找,找到则设置,并交换,找不到则创建
                if (k == null) {
                    replaceStaleEntry(key, value, i, false);
                    return;
                }
            }

            //没有找到此元素则将其设置设置到i位置(i为值是空元素)
            tab[i] = new Entry(key, value);
            //如果元素个数超过抖动值,则重置Map
            if (++size >= threshold)
                rehash();
        }

        /**
         * Remove the entry for key.

           THIS IS USED ONLY BY ThreadLocal.remove, WHICH IS NOT CURRENTLY
           PART OF THE PUBLIC API.  IF IT IS ADDED TO THE PUBLIC API AT SOME
           POINT, THIS METHOD MUST BE UNCOMMENTED.

        private void remove(ThreadLocal key) {
            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)]) {
                if (e.get() == key) {
                    e.clear();
                    expungeStaleEntry(i);
                    return;
                }
            }
        }

        */

        /**
         *
         * staleSlot是陈腐的,继续向下查找,找到则设置(读取),并交换,找不到则创建
         * 如果此陈腐点与向上查找的陈腐点相同,则清除交换后的陈腐元素
         *
         * @param  key 查找键值,他是一个ThreadLocal对象
         * @param  value 如果是set形式,表示将要设置的数据
         * @param  staleSlot 搜索的开始位置
         * @param  actAsGet true:get操作 false:set操作
         *
         */
        private Object replaceStaleEntry(ThreadLocal key, Object value,
                                         int staleSlot, boolean actAsGet) {
            Entry[] tab = table;
            int len = tab.length;
            Entry e;

            //向上查找陈腐点
            int slotToExpunge = staleSlot;
            for (int i = prevIndex(staleSlot, len); (e = tab[i]) != null;
                 i = prevIndex(i, len))
                if (e.get() == null)
                    slotToExpunge = i;

            //向下查找,直到entry为null
            for (int i = nextIndex(staleSlot, len); (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
             //取得entry的key
                Object k = e.get();

                if (k == key) {//如果找到相应的entry
                    if (actAsGet) //get 则取出
                        value = e.value;
                    else // set 则设置
                        e.value = value;

                    //与搜索的开始位置交换
                    tab[i] = tab[staleSlot];
                    tab[staleSlot] = e;

                    //如果原开始位置的entry是陈腐的,则取得陈腐点位标
                    //注意因为上明做了置换操作,所以这时i表示原起始点
                    if (slotToExpunge == staleSlot)
                        slotToExpunge = i;
                   
                    //清除陈腐entry
                    expungeStaleEntry(slotToExpunge);
                   
                    //如果是get则返回查找值,如果是set则返回传入的value值
                    return value;
                }
               
                //如果没有匹配,且起始点是陈腐的,则将当前序数作为陈腐点
                if (k == null && slotToExpunge == staleSlot)
                    slotToExpunge = i;
            }

            //如果没有找到对应项,且为get情况,则创建它
            if (actAsGet)
                value = key.initialValue();           
            //清空原起始点
            tab[staleSlot].value = null;   // Help the GC
            //将新值设置到起始点位置
            tab[staleSlot] = new Entry(key, value);

            // 如果陈腐点不是新生成元素的位置,则清除陈腐元素
            if (slotToExpunge != staleSlot)
                expungeStaleEntry(slotToExpunge);

            //返回查找到或创建的值
            return value;
        }

        /**
         *
         * 清除陈腐点上的entry
         *
         */
        private void expungeStaleEntry(int staleSlot) {
            Entry[] tab = table;
            int len = tab.length;

            //清除staleSlot点的entry
            //清楚entry前先清除entry的value
            tab[staleSlot].value = null;
            tab[staleSlot] = null;
            //长度减1
            size--;

            //重新做hash处理直到遇到元素为null
            Entry e;
            for (int i = nextIndex(staleSlot, len); (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
             //取得key
                Object k = e.get();
                if (k == null) {//如果此entry也是陈旧的,则清除它
                    e.value = null;
                    tab[i] = null;
                    size--;
                } else {
                 //将key转成ThreadLocal型
                    ThreadLocal key = (ThreadLocal)(k);
                    //取得位标
                    int h = key.threadLocalHashCode & (len - 1); 
                    if (h != i) {
                     //如果最初计算除的位标与实际位标不等,则实际位标的计算进行了
                     //向下操作
                     //清空当前位标上的entry
                        tab[i] = null;

                        // Unlike Knuth 6.4 Algorithm R, we must scan until
                        // null because multiple entries could have been stale.
                        //取得下一个为元素为null的位置,将当前entry值移动到此位置上
                        while (tab[h] != null)
                            h = nextIndex(h, len);
                        tab[h] = e;
                    }
                }
            }
        }

        /**
         *
         * 清除陈腐元素,如果Entry个数大于等于数组长度的
         * 一半则创建新数组,并重置数组元素
         *
         */
        private void rehash() {
         //清除所有陈腐的Entry
            expungeStaleEntries();

            //这里极限值是1/2的len
            if (size >= threshold - threshold / 4)
             //抖动此Map
                resize();
        }

        /**
         * Double the capacity of the table.
         */
        private void resize() {
            Entry[] oldTab = table;
            int oldLen = oldTab.length;
            int newLen = oldLen * 2;
           
            //生成一个是原来2倍长度的数组
            Entry[] newTab = new Entry[newLen];
            int count = 0;

            //迭代原数组中的每个位置的元素
            for (int j = 0; j < oldLen; ++j) {
             //取出当前元素,将其在数组中的引用置空,方便GC
                Entry e = oldTab[j];
                oldTab[j] = null;
               
                if (e != null) {//Entry不为空
                    //取得key
                 Object k = e.get();
                    if (k == null) {
                     //如果此Entry是陈腐的则将Entry的值的引用置为null,方便GC
                        e.value = null;
                    } else {//此Entry不是陈腐的
                        //转型,为了计算位标
                     ThreadLocal key = (ThreadLocal)(k);
                        int h = key.threadLocalHashCode & (newLen - 1); 
                        //取得在新数组中的位标,如果直接的hash值不中,则向下查找
                        while (newTab[h] != null)
                            h = nextIndex(h, newLen);
                       
                        //将此Entry到新数组的指定位置
                        newTab[h] = e;
                        //Entry元素个数加1
                        count++;
                    }
                }
            }

            //设置新数组的抖动数,长度,并将新数组赋给当前的Entry对象数组
            setThreshold(newLen);
            size = count;
            table = newTab;
        }

        /**
         * 清除所有陈腐的Entry
         */
        private void expungeStaleEntries() {
            Entry[] tab = table;
            int len = tab.length;
           
            //遍历数组,清除掉陈腐的Entry
            for (int j = 0; j < len; j++) {
                Entry e = tab[j];
                //如果Entry对象不为null,而key为null则表示是陈腐的,清除它
                if (e != null && e.get() == null)
                    expungeStaleEntry(j);
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值