The US group interview asked the ThreadLocal principle, this answer passed the interview!

Author: Genius program YUAN "

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

Foreword

Last week, I was lucky enough to pass through the Meituan side as a Java backend development engineer. Meituan interviewer gave me two sides. During the interview, he asked about the principle of ThreadLocal (the last time I asked about the thread pool, this time I asked about ThreadLocal, did Mead ’s father like the thread safety mechanism so much), and today I will talk about the ThreadLocal principle in detail.

ThreadLocal

ThreadLocal is a thread's internal storage class that can store data within a specified thread. Only the designated 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).
 */

复制代码
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;

复制代码
Each new thread will be instantiated as a ThreadLocalMap and assigned to the member variable ThreadLocals. If threadLocals already exist, the existing objects will be used directly.

Application scenario

When some data is thread-scoped and different threads have different copies of data, consider ThreadLocal.
Stateless, independent copy variables do not affect the high concurrency scenario of business logic.
If the business logic strongly depends on the copy variable, it is not suitable to solve with ThreadLocal.

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);
        else
            createMap(t, value);
    }
//getMap方法
ThreadLocalMap getMap(Thread t) {
      //thred中维护了一个ThreadLocalMap
      return t.threadLocals;
 }
 
//createMap
void createMap(Thread t, T firstValue) {
      //实例化一个新的ThreadLocalMap,并赋值给线程的成员变量threadLocals
      t.threadLocals = new ThreadLocalMap(this, firstValue);
}

复制代码

ThreadLocalMap

ThreadLocalMap maintains an array table for each Thread, ThreadLocal determines an array index, and this index is the corresponding location of the value storage.
ThreadLocalMaps are constructed deferred, so they are only created 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);
        }

复制代码
When ThreadLocalMap is initialized, an Entry array with a default length of 16 is created. The index value i is determined by hashCode and length bit operations.
Each Thread has a ThreadLocalMap type. Equivalent to each thread Thread has an Entry type array table. And all the reading process is carried out by operating this array table.

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();
                //如果存在key则覆盖
                if (k == key) {
                    e.value = value;
                    return;
                }
                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
            //新建结点插入
            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

复制代码
Perform bit operation on threadLocalHashCode and length to get index.
The code of 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);
    }

复制代码
Because it is a static variable, threadLocalHashCode will be re-initialized each time the threadLocal class is loaded, and it will be incremented once at the same time, increasing HASH_INCREMENT (Fibonacci hash multiplier, the result of hashing the number will be more uniform)
Static variables are also called static variables. The difference between static variables and non-static variables is that static variables are shared by all objects and have only one copy in memory. It is initialized if and only when the class is first loaded.
Non-static variables are owned by the object, and are initialized when the object is created. There are multiple copies, and the copies owned by each object do not affect each other. The initialization order of static member variables is initialized in the defined order.
For a ThreadLocal, its index value i is determined. For different threads, the same threadlocal corresponds to the same index of different tables, that is, table [i], and the tables between different threads are independent of each other.

get () method

Calculate the index and take it out 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;
                return result;
            }
        }
        return setInitialValue();
    }

复制代码

remove () method

/**
  * Remove the entry for key.
  */
   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;
           }
       }
   }

复制代码

Thread isolation feature

With the thread isolation feature, the corresponding value can only be obtained within the thread, and cannot be accessed outside the thread.
(1) Synchronized is to solve the access conflict through thread waiting and sacrifice time
(1) ThreadLocal resolves conflicts through a separate storage space for each thread, at the expense of space

Memory leak

There is a memory leak problem. Every time ThreadLocal is used, its remove () method is called to clear data.

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 <Integer> threadId =
              new ThreadLocal<Integer>()
          {
              @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();
  }
}

复制代码

to sum up

Let's play, play, play, don't joke about interviews.
The principle of ThreadLocal was almost rotten during the interview. Thread's private data is stored in ThreadLocalMap and managed by ThreadLoacl. To understand the principle of ThreadLocal, it is best to read the source code several times, especially the source code part of ThreadLocalMap. Everyone should keep their knowledge in mind before the interview.

At last

Welcome to pay attention to the public number: programmers chase the wind, get a summary of the first-line Java interview questions of the first-line manufacturers + learning knowledge guide for each knowledge point + a summary of the core Java knowledge points of a 300-page pdf document!


Guess you like

Origin juejin.im/post/5e9eb0f1e51d4546f70d2848