ThreadLocal 源代码分析(一)

2015-01-27 22:35:49 ? 作者: ? 浏览: 103
在Java的多线程编程中,为保证多个线程对共享变量的安全访问,通常会使用synchronized来保证同一时刻只有一个线程对共享变量进行操作。这种情况下可以将类变量放到ThreadLocal类型的对象中,使变量在每个线程中都有独立拷贝,不会出现一个线程读取变量时而被另一个线程修改的现象。本文将对ThreadLocal源代码分析
        源代码为android sdk ?19.如有不对或者错误的望指出。
首先在TheadLocal 中有一个重要的变量来保存与相关的值。在API 19 中这个变量叫Value。请看源码
    /**
     * Normal thread local values.
     */
    ThreadLocal.Values localValues;
等下我们来详细分析这个类。现在先回到ThreadLocal 两个经常用到的两个方法:set(T value)和get()。由浅入深,先分析get():
    /**
     * Returns the value of this variable for the current thread. If an entry
     * doesn't yet exist for this variable on this thread, this method will
     * create an entry, populating the value with the result of
     * {@link #initialValue()}.
     *
     * @return the current value of the variable for the calling thread.
     */
    @SuppressWarnings("unchecked")
    public T get() {
        // Optimized for the fast path.
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values != null) {
            Object[] table = values.table;
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                return (T) table[index + 1];
            }
        } else {
            values = initializeva lues(currentThread);
        }

        return (T) values.getAfterMiss(this);
    }
这个程序大概的意思我想大家都能看懂,通过源代码发现,查找值是通过value.table这个数组通过索引值来找到值得。其实value.table 数组存取键值对是这样一种情况:如果键值的key的索引为index,则所对应到的value索引为index+1. 由此分析可知 hash&values.mask 获取的就是key的索引值。values.mask=values.table.length-1;从源代码注释也可以看到
   /**
     * Internal hash. We deliberately don't bother with #hashCode().
     * Hashes must be even. This ensures that the result of
     * (hash & (table.length - 1)) points to a key and not a value.
     *
     * We increment by Doug Lea's Magic Number(TM) (*2 since keys are in
     * every other bucket) to help prevent clustering.
     */
    private final int hash = hashCounter.getAndAdd(0x61c88647 * 2);
在上面几行代码中,我们隐约的发现了:key值并不是ThreadLocal 本身,而是reference 这个变量。我们继续来看源码:
    /** Weak reference to this thread local instance. */
    private final Reference
  
   > reference
            = new WeakReference
   
    >(this);
   
  
发现这是ThreadLocal的弱引用。这是为什么呢?因为开启线程的开销比较大,所以我们经常会把线程放入线程池中来对线程进行重用。如果不采用弱引用的方式,那线程自身所带的数据都无法释放。这会造成内存紧张,容易发生OOM。 接下来分析 set(T value)

* * @param value the new value of the variable for the caller thread. */ public void set(T value) { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) { values = initializeva lues(currentThread); } values.put(this, value); }

  程序比较简单,重点来分析put方法。
       /**
         * Sets entry for given ThreadLocal to given value, creating an
         * entry if necessary.
         */
        void put(ThreadLocal
    key, Object value) {
            cleanUp();

            // Keep track of first tombstone. That's where we want to go back
            // and add an entry if necessary.
            int firstTombstone = -1;

            for (int index = key.hash & mask;; index = next(index)) {
                Object k = table[index];

                if (k == key.reference) {
                    // Replace existing entry.
                    table[index + 1] = value;
                    return;
                }

                if (k == null) {
                    if (firstTombstone == -1) {
                        // Fill in null slot.
                        table[index] = key.reference;
                        table[index + 1] = value;
                        size++;
                        return;
                    }

                    // Go back and replace first tombstone.
                    table[firstTombstone] = key.reference;
                    table[firstTombstone + 1] = value;
                    tombstones--;
                    size++;
                    return;
                }

                // Remember first tombstone.
                if (firstTombstone == -1 && k == TOMBSTONE) {
                    firstTombstone = index;
                }
            }
        } 
大致意思比较简单:通过hash 值定位到key的索引值,然后判断这个索引值是否有对应的TheadLocal;如果有,则返回value。如果没有,通过找到firstTombstone这个索引值,然后赋值对应的key和value。在这里有人要问了这个firstTombstone是什么意思?别着急,慢慢分析。接下我们分析c