preface

This article focuses on ThreadLocal, which is rarely used in projects but can be extremely useful. This question is often asked in an interview.

The body of the

This article uses JDK1.8.

ThreadLocal is introduced

Take a look at the introduction of the source code, too many documents, not all posted out

/**
 * 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).
 *
 * <p>For example, the class below generates unique identifiers local to each
 * thread.
 * A thread's id is assigned the first time it invokes {@code ThreadId.get()}
 * and remains unchanged on subsequent calls.

Copy the code

use

The usage method is also indicated in the notes:

import java.util.concurrent.atomic.AtomicInteger;

public class ThreadId {
      // 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(a) {
                  returnnextId.getAndIncrement(); }};// Returns the current thread's unique ID, assigning it if necessary
      
      public static int get(a) {
          returnthreadId.get(); }}Copy the code

To summarize:

  1. ThreadLocal provides a thread-local variable
  2. Only one variable can be fetched per thread through the set and GET methods of the class
  3. Initialize variables independently
  4. The life cycle is the same as the life cycle of a thread

The source code

ThreadLocal

Let’s start with the set method:


public void set(T value) {
        // Get the current thread object (so in multi-threaded cases it is the current thread of operation)
        Thread t = Thread.currentThread();
        
        // Get ThreadLocalMap from the current thread object
        ThreadLocalMap map = getMap(t);
        
        // If ThreadLocalMap is not null, store it directly
        if(map ! =null)
            // key is the current Threadlocal instance
            map.set(this, value);
        else
        // If ThreadLocalMap is null, a ThreadLocalMap object is created for the current thread
            createMap(t, value);
}

Copy the code

Let’s look at the get method:


public T get(a) {
        // Get the current thread object
        Thread t = Thread.currentThread();
        
        // Get the ThreadLocalMap of the current thread object
        ThreadLocalMap map = getMap(t);
        
        if(map ! =null) {
        // Get the map Entry object with the current ThreadLocal instance as the key
            ThreadLocalMap.Entry e = map.getEntry(this);
            if(e ! =null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                returnresult; }}return setInitialValue();
    }
    
Copy the code

Let’s look at the methods used in the above two methods:

  1. First, the getMap method:

// Multiple methods call this method
ThreadLocalMap getMap(Thread t) {
        // Returns the ThreadLocalMap of the incoming thread
        return t.threadLocals;
}

Copy the code
  1. CreateMap method:

void createMap(Thread t, T firstValue) {
        // assign value to threadLocals (new)
        t.threadLocals = new ThreadLocalMap(this, firstValue);
}

Copy the code
  1. SetInitialValue method

private T setInitialValue(a) {
        // call the initialValue method to get the initialValue
        T value = initialValue();
        // Get the current thread object
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if(map ! =null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

Copy the code
  1. The initialValue method:

protected T initialValue(a) {
        return null;
    }

Copy the code

Conclusion 1: Null is returned if the method is not overridden during initialization, so this method will normally override the method in the code that initializes ThreadLocal.

Take a look at the code in Thread:


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

If thread.currentthread () is used to obtain the currentThread object, the set method will operate on the currentThread object. ThreadLocals is a static internal ThreadLocalMap class of the ThreadLocal class, where the key is this (which is the current ThreadLocal instance object).

Summary:

  • From the source, the problem of Threadlocal cannot be seen in terms of solving the concurrency problem of multithreaded programs and solving the sharing problem of multithreaded access to resources.
  • I amThreadlocalThe rough understanding is: passThreadlocalInstance object ofaTo operate the currentThreadIn theThreadLocal.ThreadLocalMapIn the objectkeyforaCorresponding data;

Usage scenarios

1. Database connection pool

The most typical example is the database connection pool. How to handle the relationship between threads and database connections is the use of Threadlocal. For details, go to Google or read the source code.

2. Transfer information or parameters

Here are some of the scenarios I used:

  1. Information passing Some interfaces do not pass information as parameters, but in such cases you need information that is only available at the beginning, and you can use ThreadLocal to operate.

A memory leak

As for memory leakage, IN fact, I also encountered a memory leak in the process of using; Here’s the scenario:

Here is the flow chart:

We use ThreadLocal to record user information, intercept requests, and store user information, but sometimes in tourist mode, it shows logged in, not your own account.

Why does this happen?

  1. Request A (with user information A) retrieves thread A from the request connection pool
  2. The interceptor intercepts thread A and stores A Threadlocal for user information A
  3. Thread A is then processed in background processing to get user information A from Threadlocal
  4. After the processing is complete, thread A returns to the connection pool with user information A, Threadlocal’s user information A is not cleared, and thread A is not reclaimed
  5. Request B (without user information) fetchs thread A from the request connection pool. No user information is retrieved and no operation is performed on Threadlocal
  6. Go back to Step 2

Solutions:

  1. Threadlocal is cleared each time at the interceptor (by calling Threadlocal’s remove method)
  2. Save even if the user information is null;

The last

Blog Address:

GitHub Address:

Java ThreadLocal Java ThreadLocal (ThreadLocal