ThreadLocal源码分析

ThreadLocal和线程同步机制相比:都是为了解决多线程中相同变量的访问冲突问题。
  在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的。
  ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。ThreadLocal提供了线程安全的共享相同名称的不同对象。
  在一个村中有100户人家,当大家要使用村里唯一的一台拖拉机时,需要使用同步机制。当每家骑自家的自行车时,使用ThreadLocal,虽然大家骑的都是自行车,但是是不同的自行车实例。

ThreadLocal源码分析_第1张图片
Paste_Image.png
Paste_Image.png

package com.hfbank.biz.service;

import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicInteger;

/**

  • Implements a thread-local storage, that is, a variable for which each thread

  • has its own value. All threads share the same {@code ThreadLocal} object,

  • but each sees a different value when accessing it, and changes made by one

  • thread do not affect the other threads. The implementation supports

  • {@code null} values.

  • @see java.lang.Thread

  • @author Bob Lee

  • jxy 为了更好的使用,1.5增加了泛型的支持,可以看出为了能够更好更简单的被使用,不放过需要优化的任何地方

  • Values中为什么不把:private Object[] table;设置为泛型?

  • 一个ThreadLocal只能保持一个Object,如果想保持多个应如何处理?
    */
    public class ThreadLocal {

    /* Thanks to Josh Bloch and Doug Lea for code reviews and impl advice. */
    // jxy 已经多次看到此类注释,代码如人生

    /**

    • Creates a new thread-local variable.
      */
      public ThreadLocal() {}

    /**

    • 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();
      /
      *

      • jxy values是需要和Thread类结合使用的,并且是一个类似map的存储类
      • 在Thread类中有: java.lang.ThreadLocal.Values localValues;
      • localValues在Thread类中只是定义为包内可见,但没有任何操作,就是为了给ThreadLocal类使用
      • Thread类的实例在运行时,每个线程都是不同的实例,这样localValues也就不会相互干扰
      • 这样两个类配合实现不同线程获取不同实例的需求。
      • 如果不和Thread类配合能否实现现有功能?
      • values为什么做成可以存储多个key-value对?
        */
        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 = initializeValues(currentThread);
        }

      return (T) values.getAfterMiss(this);
      }

    /**

    • Provides the initial value of this variable for the current thread.
    • The default implementation returns {@code null}.
    • @return the initial value of the variable.
    • jxy 该函数是protected类型的,很显然是建议在子类重载该函数的,所以通常该函数都会以匿名内部类的形式被重载,以指定初始值
      */
      protected T initialValue() {
      return null;
      }

    /**

    • Sets the value of this variable for the current thread. If set to
    • {@code null}, the value will be set to null and the underlying entry will
    • still be present.
    • @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 = initializeValues(currentThread);
      }
      values.put(this, value);
      }

    /**

    • Removes the entry for this variable in the current thread. If this call
    • is followed by a {@link #get()} before a {@link #set},
    • {@code #get()} will call {@link #initialValue()} and create a new
    • entry with the resulting value.
    • @since 1.5
      */
      public void remove() {
      Thread currentThread = Thread.currentThread();
      Values values = values(currentThread);
      if (values != null) {
      values.remove(this);
      }
      }

    /**

    • Creates Values instance for this thread and variable type.
      */
      Values initializeValues(Thread current) {
      return current.localValues = new Values();
      }

    /**

    • Gets Values instance for this thread and variable type.
      */
      Values values(Thread current) {
      return current.localValues;
      }

    /** Weak reference to this thread local instance. */
    // jxy 此处使用WeakReference可以使得当ThreadLocal可以被回收,而不会因为在values中保存有引用而无法回收
    private final Reference> reference = new WeakReference>(this);

    /** Hash counter. */
    private static AtomicInteger hashCounter = new AtomicInteger(0);

    /**

    • 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.
    • jxy 通过定义一个static的AtomicInteger类型变量hashCounter,
    • 实现每一个ThreadLocal类实例的hash值不相同。即每次比上一次的值加1.
      */
      private final int hash = hashCounter.getAndAdd(0x61c88647 * 2);

    /**

    • Per-thread map of ThreadLocal instances to values.
      */
      static class Values {

      /**

      • Size must always be a power of 2. 2 的幂次方
      • jxy 为什么必须是2的幂次方?
      • 在进行hash计算的时候,是使用:int index = hash & values.mask;
      •                            mask = table.length - 1;
        
      •                            table = new Object[capacity * 2];
        
      • 只有当INITIAL_SIZE为2的幂次方时,mask才可以是类似:00001111的值,在扩充table时也是通过*2来计算capacity的
      • hash每次的值不一样,这样计算出的index(即取hash最后几位的值)就是唯一的。
        */
        private static final int INITIAL_SIZE = 16;

      /**

      • Placeholder for deleted entries.
      • jxy 为什么要定义tombstone?
      • 因为一个线程中可以定义多个ThreadLocal,但是values中的table并不是在定义ThreadLocal时校验是否需要扩充,
      • 当定义量大于capacity时,此时index = hash & values.mask有可能得到重复的值,所以要跟踪哪些entry已经废弃
      • 在put时需要进行tombstone的判断处理。
        */
        private static final Object TOMBSTONE = new Object();

      /**

      • Map entries. Contains alternating keys (ThreadLocal) and values.
      • The length is always a power of 2.
      • jxy table中每一个entry是有三个状态的,null没有进行过设置,tombstone,设置过但是现在废弃了,正常设置状态
        */
        private Object[] table;

      /** Used to turn hashes into indices. */
      private int mask;

      /** Number of live entries. */
      private int size;

      /** Number of tombstones. */
      private int tombstones;

      /** Maximum number of live entries and tombstones. */
      private int maximumLoad;

      /** Points to the next cell to clean up. */
      private int clean;

      /**

      • Constructs a new, empty instance.
        */
        Values() {
        initializeTable(INITIAL_SIZE);
        this.size = 0;
        this.tombstones = 0;
        }

      /**

      • Used for InheritableThreadLocals.
        */
        Values(Values fromParent) {
        this.table = fromParent.table.clone();
        this.mask = fromParent.mask;
        this.size = fromParent.size;
        this.tombstones = fromParent.tombstones;
        this.maximumLoad = fromParent.maximumLoad;
        this.clean = fromParent.clean;
        inheritValues(fromParent);
        }

      /**

      • Inherits values from a parent thread.
        */
        @SuppressWarnings({"unchecked"})
        private void inheritValues(Values fromParent) {
        // Transfer values from parent to child thread.
        Object[] table = this.table;
        for (int i = table.length - 2; i >= 0; i -= 2) {
        Object k = table[i];

         if (k == null || k == TOMBSTONE) {
             // Skip this entry.
             continue;
         }
        
         // The table can only contain null, tombstones and references.
         Reference> reference = (Reference>) k;
         // Raw type enables us to pass in an Object below.
         InheritableThreadLocal key = reference.get();
         if (key != null) {
             // Replace value with filtered value.
             // We should just let exceptions bubble out and tank
             // the thread creation
             table[i + 1] = key.childValue(fromParent.table[i + 1]);
         } else {
             // The key was reclaimed.
             table[i] = TOMBSTONE;
             table[i + 1] = null;
             fromParent.table[i] = TOMBSTONE;
             fromParent.table[i + 1] = null;
        
             tombstones++;
             fromParent.tombstones++;
        
             size--;
             fromParent.size--;
         }
        

        }
        }

      /**

      • Creates a new, empty table with the given capacity.
        */
        private void initializeTable(int capacity) {
        this.table = new Object[capacity * 2];
        this.mask = table.length - 1;
        this.clean = 0;
        this.maximumLoad = capacity * 2 / 3; // 2/3
        }

      /**

      • Cleans up after garbage-collected thread locals.

      • jxy 每次在新增或删除entry后需要重新检查table状态,进行更新
        */
        private void cleanUp() {
        if (rehash()) {
        // If we rehashed, we needn't clean up (clean up happens as
        // a side effect).
        return;
        }

        if (size == 0) {
        // No live entries == nothing to clean.
        return;
        }

        // Clean log(table.length) entries picking up where we left off
        // last time.
        int index = clean;
        Object[] table = this.table;
        // jxy 此处没有全部遍历所有的item,猜测是防止性能问题
        for (int counter = table.length; counter > 0; counter >>= 1, index = next(index)) {
        Object k = table[index];

         if (k == TOMBSTONE || k == null) {
             continue; // on to next entry
         }
        
         // The table can only contain null, tombstones and references.
         @SuppressWarnings("unchecked")
         Reference> reference = (Reference>) k;
         if (reference.get() == null) {
             // This thread local was reclaimed by the garbage collector.
             table[index] = TOMBSTONE;
             table[index + 1] = null;
             tombstones++;
             size--;
         }
        

        }

        // Point cursor to next index.
        clean = index;
        }

      /**

      • Rehashes the table, expanding or contracting it as necessary.

      • Gets rid of tombstones. Returns true if a rehash occurred.

      • We must rehash every time we fill a null slot; we depend on the

      • presence of null slots to end searches (otherwise, we'll infinitely

      • loop).
        */
        private boolean rehash() {
        if (tombstones + size < maximumLoad) {
        return false;
        }

        int capacity = table.length >> 1;

        // Default to the same capacity. This will create a table of the
        // same size and move over the live entries, analogous to a
        // garbage collection. This should only happen if you churn a
        // bunch of thread local garbage (removing and reinserting
        // the same thread locals over and over will overwrite tombstones
        // and not fill up the table).
        int newCapacity = capacity;

        if (size > (capacity >> 1)) {
        // More than 1/2 filled w/ live entries.
        // Double size.
        newCapacity = capacity * 2;
        }

        Object[] oldTable = this.table;

        // Allocate new table.
        initializeTable(newCapacity);

        // We won't have any tombstones after this.
        this.tombstones = 0;

        // If we have no live entries, we can quit here.
        if (size == 0) {
        return true;
        }

        // Move over entries.
        for (int i = oldTable.length - 2; i >= 0; i -= 2) {
        Object k = oldTable[i];
        if (k == null || k == TOMBSTONE) {
        // Skip this entry.
        continue;
        }

         // The table can only contain null, tombstones and references.
         @SuppressWarnings("unchecked")
         Reference> reference = (Reference>) k;
         ThreadLocal key = reference.get();
         if (key != null) {
             // Entry is still live. Move it over.
             add(key, oldTable[i + 1]);
         } else {
             // The key was reclaimed.
             size--;
         }
        

        }

        return true;
        }

      /**

      • Adds an entry during rehashing. Compared to put(), this method
      • doesn't have to clean up, check for existing entries, account for
      • tombstones, etc.
        */
        void add(ThreadLocal key, Object value) {
        for (int index = key.hash & mask;; index = next(index)) {
        Object k = table[index];
        if (k == null) {
        table[index] = key.reference;
        table[index + 1] = value;
        return;
        }
        }
        }

      /**

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

        // jxy 为什么需要循环,和TOMBSTONE定义说明结合
        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;
         }
        

        }
        }

      /**

      • Gets value for given ThreadLocal after not finding it in the first

      • slot.
        */
        Object getAfterMiss(ThreadLocal key) {
        Object[] table = this.table;
        int index = key.hash & mask;

        // If the first slot is empty, the search is over.
        if (table[index] == null) {
        Object value = key.initialValue();

         // If the table is still the same and the slot is still empty...
         // jxy 此处为什么要增加判断?如果是多线程的话,此类的实例都应该是在一个线程中运行啊
        

// 增加判断是因为上面key.initalValue()的调用,在ThreadLocal中无法预知使用者如何实现initalValue,如果在其中做了诸如put之类的操作有可能导致rehash操作,此时table就会不相等,出现错误
if (this.table == table && table[index] == null) {
table[index] = key.reference;
table[index + 1] = value;
size++;

                cleanUp();
                return value;
            }

            // The table changed during initialValue().
            put(key, value);
            return value;
        }

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

        // Continue search.
        for (index = next(index);; index = next(index)) {
            Object reference = table[index];
            if (reference == key.reference) {
                return table[index + 1];
            }

            // If no entry was found...
            if (reference == null) {
                Object value = key.initialValue();

                // If the table is still the same...
                if (this.table == table) {
                    // If we passed a tombstone and that slot still
                    // contains a tombstone...
                    if (firstTombstone > -1
                            && table[firstTombstone] == TOMBSTONE) {
                        table[firstTombstone] = key.reference;
                        table[firstTombstone + 1] = value;
                        tombstones--;
                        size++;

                        // No need to clean up here. We aren't filling
                        // in a null slot.
                        return value;
                    }

                    // If this slot is still empty...
                    if (table[index] == null) {
                        table[index] = key.reference;
                        table[index + 1] = value;
                        size++;

                        cleanUp();
                        return value;
                    }
                }

                // The table changed during initialValue().
                put(key, value);
                return value;
            }

            if (firstTombstone == -1 && reference == TOMBSTONE) {
                // Keep track of this tombstone so we can overwrite it.
                firstTombstone = index;
            }
        }
    }

    /**
     * Removes entry for the given ThreadLocal.
     */
    void remove(ThreadLocal key) {
        cleanUp();

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

            if (reference == key.reference) {
                // Success!
                table[index] = TOMBSTONE;
                table[index + 1] = null;
                tombstones++;
                size--;
                return;
            }

            if (reference == null) {
                // No entry found.
                return;
            }
        }
    }

    /**
     * Gets the next index. If we're at the end of the table, we wrap back
     * around to 0.
     */
    private int next(int index) {
        return (index + 2) & mask;
    }
}

}

你可能感兴趣的:(ThreadLocal源码分析)