I remember being asked about ThreadLocal in an interview and giving a lackluster answer, so I decided to look into the source code for ThreadLocal

Interviewer: Ever used ThreadLocal?

Yes, when ThreadLocal was used, Spring was used to crosscut the entire Controller layer and ThreadLocal was used to count the execution time of the corresponding method for each request. The code is as follows

public class ProfilerAdvice { Logger logger = Logger.getLogger(ProfilerAdvice.class); private static final ThreadLocal<Long> TIME_THREADLOCAL = new ThreadLocal<Long>(){ @Override protected Long initialValue() { return System.currentTimeMillis(); }}; public static final void begin() { TIME_THREADLOCAL.set(System.currentTimeMillis()); } public static final Long end() { return System.currentTimeMillis() - TIME_THREADLOCAL.get(); } public void requestStart() { ProfilerAdvice.begin(); } public void requestEnd(JoinPoint JoinPoint) {/** * To get the intercepted method */ String methodName = joinPoint.getSignature().getName(); Logger.info (profileradvice.end ()/1000.0+"s"); }}Copy the code

Interviewer: What are the data structures corresponding to ThreadLocal?

The building Lord answer: should be to use hash table realization, the building Lord this time in the heart began to have no bottom, and then did not then……

Discuss the implementation of ThreadLocal in JDK source code

Main methods:

  • Get (t); get (t); 2. Return the member ThreadLocalMap (map) of the current thread t; 3. If map is not null, the Entry (e) of the ThreadLocalMap with the current thread as the key is obtained. If E is not null, the value of this Entry is directly returned. 4. If map is null or e is null, return setInitialValue(). SetInitialValue () calls the overridden initialValue() to return the new value (null if no overridden initialValue is returned) and stores the new value into the ThreadLocalMap of the current thread (if the current thread does not have a ThreadLocalMap, Will create one first).
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(); }Copy the code
  • ThreadLocal set (t); 2. Return the member ThreadLocalMap (map) of the current thread t; If map is not null, update the ThreadLocalMap with the current thread as key, or create a ThreadLocalMap with the current thread as key.
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map ! = null) map.set(this, value); else createMap(t, value); }Copy the code

The main code implementation of ThreadLocal

I won’t go into detail about what I think is important and easy to understand in ThreadLocal

Public class ThreadLocal<T> {/** * ThreadLocals relies on additional per-thread linear probe hashes mapped to per-thread (thread. ThreadLocals and * inheritableThreadLocals). ThreadLocal objects act as keys and are searched through threadLocalHashCode. This is a custom hash code * (useful only within ThreadLocalMaps) that eliminates hash collisions * in the common case of continuous construction of ThreadLocals * used by the same thread while maintaining good behavior and exception occurrences. */ private final int threadLocalHashCode = nextHashCode(); /** * The next hash code will be emitted, atomically updated, starting from zero. */ private static AtomicInteger nextHashCode = new AtomicInteger(); /** * the difference between continuously generated hash codes, which translates the implicit sequential thread local ID into a near-optimal spread * twice the size of the table's multiplicative hash value. */ private static final int HASH_INCREMENT = 0x61c88647; /** * Returns the next hash code. */ private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); } /** * returns the current thread "initial value" of this thread-local variable. This method is called the first time a thread accesses a variable with the {@link #get} method, * unless the thread previously called the {@link #set} method, in which case the initialValue method will not be called for that thread. * Normally, this method is called at most once per thread, but may be called again on subsequent calls {@link #remove} followed by {@link #get}. * <p> This implementation just returns {@code null}; If a programmer wants the initial value of a thread-local variable to be something other than {@code NULL}, * then the child code {@codelocal} must subclass and override this method. Typically, anonymous inner classes will be used. */ protected T initialValue() { return null; } 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(); } private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map ! = null) map.set(this, value); else createMap(t, value); return value; } public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map ! = null) map.set(this, value); else createMap(t, value); } /** * SuppliedThreadLocal; SuppliedThreadLocal; SuppliedThreadLocal;} /** * SuppliedThreadLocal; SuppliedThreadLocal; Note that the functional interface Supplier cannot be null. */ static final class SuppliedThreadLocal<T> extends ThreadLocal<T> { private final Supplier<? extends T> supplier; SuppliedThreadLocal(Supplier<? extends T> supplier) { this.supplier = Objects.requireNonNull(supplier); } @Override protected T initialValue() { return supplier.get(); }} /** * ThreadLocalMap is a custom hashMap. For more details on hashMap, see Talking about hashMap source code. * Is only used to maintain local variable values for the current thread. Only the ThreadLocal class has permission to operate on it. * is a Thread's private property. To avoid a series of problems caused by data residing in memory with a large space footprint or a long life cycle, * hash table keys are WeakReferences. When space is insufficient, unreferenced entries are cleared. */ static class ThreadLocalMap { static class Entry extends WeakReference<ThreadLocal<? >> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<? > k, Object v) { super(k); value = v; }} /** * constructs a new mapping that contains the initial mapping,ThreadLocal mapping, so we only create one when we create at least one entry. */ 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); } /** * Construct a new ThreadLocal containing all maps from the given parentMap. Only called by createInheritedMap. * * @param parentMap the map associated with parent thread. */ private ThreadLocalMap(ThreadLocalMap parentMap) { Entry[] parentTable = parentMap.table; int len = parentTable.length; setThreshold(len); table = new Entry[len]; for (int j = 0; j < len; j++) { Entry e = parentTable[j]; if (e ! = null) { @SuppressWarnings("unchecked") ThreadLocal<Object> key = (ThreadLocal<Object>) e.get(); if (key ! = null) { Object value = key.childValue(e.value); Entry c = new Entry(key, value); int h = key.threadLocalHashCode & (len - 1); while (table[h] ! = null) h = nextIndex(h, len); table[h] = c; size++; } } } } } }Copy the code

Use ThreadLocal demo


                                                

public class ThreadLocalExample {


    public static class MyRunnable implements Runnable {

        private ThreadLocal<Integer> threadLocal =
               new ThreadLocal<Integer>();

        @Override
        public void run() {
            threadLocal.set( (int) (Math.random() * 100D) );
    
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
            }
    
            System.out.println(threadLocal.get());
        }
    }


    public static void main(String[] args) {
        MyRunnable sharedRunnableInstance = new MyRunnable();

        Thread thread1 = new Thread(sharedRunnableInstance);
        Thread thread2 = new Thread(sharedRunnableInstance);

        thread1.start();
        thread2.start();

        thread1.join(); //wait for thread 1 to terminate
        thread2.join(); //wait for thread 2 to terminate
    }

}


                                            Copy the code

This example creates an instance of MyRunnable that is passed to two different threads. Both threads execute the run () method, setting different values on the ThreadLocal instance. If access to the set () call is synchronized and it is not a ThreadLocal object, the second thread overrides the value set by the first thread. However, because it is a ThreadLocal object, the two threads cannot see each other’s values. As a result, they set and acquire different values.

summary

  • ThreadLocal features and usage scenarios

1. Realize single thread singleton and storage of single thread context information, such as transaction ID, etc

Thread-safe: Non-thread-safe objects become thread-safe when ThreadLocal is used, since each thread has an instance

3. Load some thread-specific data to avoid passing parameters back and forth in methods

  • Problems with ThreadLocal usage

ThreadLocal does not solve the problem of multiple threads accessing shared objects. If threadlocal.set () is shared by multiple threads, it still involves concurrency issues.

2. Will it cause memory leaks? ThreadLocal is thought to cause memory leaks for the following reasons

First, the ThreadLocal instance is held by the thread’s ThreadLocalMap instance, which can also be viewed as held by the thread. If the application uses a thread pool, the previous thread instance will survive processing for reuse purposes. Therefore, the value of ThreadLocal is held, resulting in a memory leak.

The logic above is clear, but Java designers have already thought of this problem. ThreadLocal does not leak memory because ThreadLocalMap does not directly select a ThreadLocal instance when selecting a key, but rather a weak reference to the ThreadLocal instance. So there is actually no memory leak from a ThreadLocal design point of view, as shown below:


                                                
static class ThreadLocalMap { 
    
    static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
    }
}


                                            Copy the code

Refer to the article