美文网首页
ThreadLocal源码分析

ThreadLocal源码分析

作者: tiancijiaren | 来源:发表于2017-05-10 14:59 被阅读0次

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

    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<T> {

      /* 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<ThreadLocal<T>> reference = new WeakReference<ThreadLocal<T>>(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<InheritableThreadLocal<?>> reference = (Reference<InheritableThreadLocal<?>>) 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<ThreadLocal<?>> reference = (Reference<ThreadLocal<?>>) 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<ThreadLocal<?>> reference = (Reference<ThreadLocal<?>>) 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源码分析

          本文链接:https://www.haomeiwen.com/subject/qykitxtx.html