By YUAN, the genius program”

Original: https://blog.csdn.net/JAck_chen0309/article/details/105257331

preface

Last week I was lucky enough to get a job as a Java backend developer. The interviewer gave me a second interview. In the interview, he asked about the principle of ThreadLocal (ThreadLocal is a big fan of thread safety). Today, he explained the principle of ThreadLocal in more detail.

ThreadLocal

ThreadLocal is an internal storage class for threads that can store data within a specified thread. Only the specified thread can get the stored data.

/**
 * This class provides thread-local variables.  These variables differ from
 * their normal counterparts in that each thread that accesses one (via its
 * {@code get} or {@code set} method) has its own, independently initialized
 * copy of the variable.  {@code ThreadLocal} instances are typically private
 * static fields in classes that wish to associate state with a thread (e.g.,
 * a user ID or Transaction ID).
 */

Copy the code
Each thread has an instance object of ThreadLocalMap, and ThreadLocalMap is managed by ThreadLocal.

/* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

Copy the code
Each new thread instantiates a ThreadLocalMap and assigns it to the member variable ThreadLocals, which uses the existing object if ThreadLocals already exists.

Application scenarios

Consider ThreadLocal when some data is thread-scoped and different threads have different copies of the data.

Stateless, high concurrency scenarios where independent replica variables do not affect business logic.

If the business logic is heavily dependent on replica variables, ThreadLocal is not an appropriate solution.

The get () and set ()

Set () is implemented by calling set() of ThreadLocalMap

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if(map ! = null) map.set(this, value);elsecreateMap(t, value); ThreadLocalMap getMap(Thread t) {// A ThreadLocalMap is maintained in thredreturnt.threadLocals; } //createMap void createMap(Thread t, t firstValue) { ThreadLocals. Htreadlocals = new ThreadLocalMap(this, firstValue); }Copy the code

ThreadLocalMap

ThreadLocalMap maintains an array table for each Thread, and ThreadLocal determines an array subscript that corresponds to the location of the value store.

ThreadLocalMaps are constructed lazily, so they are created only when at least one entry is to be placed.

ThreadLocalMap(ThreadLocal<? > firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1;setThreshold(INITIAL_CAPACITY);
        }

Copy the code
ThreadLocalMap initializes to create an array of entries with a default length of 16. The index value I is determined by hashCode and length bit operation.

Each Thread has a ThreadLocalMap type. Each Thread has an Entry array table. And all the reading is done by manipulating the array table.

The set () method

        private void set(ThreadLocal<? > key, Object value) { // We don't use a fast path as with get() because it is at // least as common to use set() to create new entries as // it is to replace existing ones, in which case, a fast // path would fail more often than not. 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)]) { ThreadLocal
       k = e.get(); // override if (k == key) {e.value = value; return; } if (k == null) { replaceStaleEntry(key, value, i); return; } // insert TAB [I] = new Entry(key, value); int sz = ++size; if (! cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }Copy the code
Index threadLocalHashCode bitwise with the length.

The code for threadLocalHashCode is as follows:

    private final int threadLocalHashCode = nextHashCode();

    /**
     * The next hash code to be given out. Updated atomically. Starts at
     * zero.
     */
    private static AtomicInteger nextHashCode =
        new AtomicInteger();

    /**
     * The difference between successively generated hash codes - turns
     * implicit sequential thread-local IDs into near-optimally spread
     * multiplicative hash values for power-of-two-sized tables.
     */
    private static final int HASH_INCREMENT = 0x61c88647;

    /**
     * Returns the next hash code.
     */
    private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }

Copy the code
Since it is static, threadLocalHashCode reinitializes and increments itself once each time the threadLocal class is loaded to add HASH_INCREMENT (Fibonacci hash multiplier).

Static variables are also called static variables. The difference between static and non-static variables is that static variables are shared by all objects and have only one copy in memory, which is initialized if and only if the class is first loaded.

Non-static variables are owned by the object, initialized when the object is created, and have multiple copies that do not affect each other. Static member variables are initialized in the order defined.

For a ThreadLocal, its index value I is fixed. For different threads, the same threadLocal corresponds to the same subscript of different tables, namely table[I]. Tables of different threads are independent of each other.

The get () method

Compute the index and fetch it directly

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if(map ! = null) { ThreadLocalMap.Entry e = map.getEntry(this);if(e ! = null) { @SuppressWarnings("unchecked")
                T result = (T)e.value;
                returnresult; }}return setInitialValue();
    }

Copy the code

Remove () method

/**
  * Remove the entry forkey. */ 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; }}}Copy the code

Thread isolation feature

Thread isolation feature, only in the thread can obtain the corresponding value, cannot access outside the thread.

(1) Synchronized solves access conflicts by sacrificing time through thread waiting

(1) ThreadLocal resolves conflicts by creating a separate storage space for each thread

Memory leak problem

ThreadLocal’s remove() method is called every time ThreadLocal is used to remove data.

The Demo program

import java.util.concurrent.atomic.AtomicInteger;

/**
 * <h3>Exper1</h3>
 * <p>ThreadLocalId</p>
 *
 * @author : cxc
 * @date : 2020-04-01 23:48
 **/
  public class ThreadLocalId {
      // Atomic integer containing the next thread ID to be assigned
          private static final AtomicInteger nextId = new AtomicInteger(0);

          // Thread local variable containing each thread's ID private static final ThreadLocal 
      
        threadId = new ThreadLocal
       
        () { @Override protected Integer initialValue() { return nextId.getAndIncrement(); }}; // Returns the current thread'
       
      s unique ID, assigning it if necessary
          public static int get() {
            return threadId.get();
          }
          public static void remove() {
            threadId.remove();
          }
  }

/**
 * <h3>Exper1</h3>
 * <p></p>
 *
 * @author : cxc
 * @date : 2020-04-02 00:07
 **/
public class ThreadLocalMain {
  private static void incrementSameThreadId(){
    try{
      for(int i=0; i<5; i++){ System.out.println(Thread.currentThread() +"_"+i+",threadId:"+
            ThreadLocalId.get());
      }
    }finally {
      ThreadLocalId.remove();
    }
  }

  public static void main(String[] args) {
    incrementSameThreadId();
    new Thread(new Runnable() {
      @Override
      public void run() {
        incrementSameThreadId();
      }
    }).start();
    new Thread(new Runnable() {
      @Override
      public void run() { incrementSameThreadId(); } }).start(); }}Copy the code

conclusion

Let’s have fun. Let’s have fun. Don’t joke about the interview.

The principle of ThreadLocal was pretty much busted in the interview. The private data for threads is stored in ThreadLocalMap and managed by ThreadLoacl. To understand how ThreadLocal works, it’s a good idea to read the source code a few times, especially the Source code for ThreadLocalMap. You should keep the knowledge points in mind before the interview.



The last

Welcome to pay attention to the public number: programmers chasing the wind, receive a line of large factory Java interview questions summary + each knowledge point learning thinking guide + a 300 page PDF document Java core knowledge points summary!