What is ThreadLocal?

Disclainer: This article uses JDK 1.8

Let’s take a look at the JDK documentation:

/**
 * 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. * <pre> * import java.util.concurrent.atomic.AtomicInteger;  * * /
Copy the code

The ThreadLocal class, as its name implies, can be understood as a thread-local variable. That is, if you define a ThreadLocal, each thread that reads or writes to that ThreadLocal is thread isolated and does not affect each other. It provides a mechanism for thread closure by having a separate copy of mutable data for each thread.

ThreadLocal source code analysis

ThreadLocal principle

Ref; :

public class ThreadLocal<T> { 
        / / the constructor
        public ThreadLocal() {} 

        // Returns the current thread "initial value" of this thread, a thread-local variable. This method is called the first time it is called
        protected T initialValue() {} 

        // Create a ThreadLocalMap. ThreadLocal is maintained by a map.
        void createMap(Thread t, T firstValue) {} 

        // Returns the thread-local variable value corresponding to the current thread.
        public T get() {} 

        / / get ThreadLocalMap
        ThreadLocalMap getMap(Thread t) {} 

        // Sets the value of thread-local variables for the current thread
        public void set(T value) {} 

        /** Removes the value of the current thread-local variable to reduce memory usage. In fact, when the thread ends, the local variables of the thread will be garbage collected automatically, so we don't need to call remove, we just call remove to speed up the memory collection. * * /
        public void remove() {} 

        // set the initialValue and call initialValue
        private T setInitialValue() {} 

        // Static inner class, a map to maintain
        static class ThreadLocalMap { 
            // The static inner class of ThreadLocalMap inherits weak references, which is the root cause of no memory leaks
            // The Entry key is ThreadLocal and is a weak reference. The value is the value
            static class Entry extends WeakReference<ThreadLocal<? >>{... }}... }Copy the code

First, take a look at ThreadLocal’s set() method

public void set(T value) {
    // Get the current thread object
    Thread t = Thread.currentThread();
    // Get ThreadLocalMap here
    ThreadLocalMap map = getMap(t);
    // If the map exists, store the current thread object t as the key and the object to be stored as the value in the map
    if(map ! =null)
        map.set(this, value);
    else
        createMap(t, value);
}
Copy the code
static class ThreadLocalMap {

    /** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. * /
    static class Entry extends WeakReference<ThreadLocal<? >>{
        /** The value associated with this ThreadLocal. */
        Object value;

        Entry(ThreadLocal<? > k,Object v) {
            super(k); value = v; }}/ /... For a long
}
Copy the code

Notice that ThreadLocalMap is an inner class of ThreadLocal. Use the Entry class for storage

Our values are stored on this Map, and the key is the current ThreadLocal object!

If the Map does not exist, initialize one:

/** Create the map associated with a ThreadLocal. Overridden in InheritableThreadLocal. Params: T -- the current thread firstValue -- value for the initial entry of the map **/
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}
Copy the code

If the Map exists, we get it from Thread:

/** Get the map associated with a ThreadLocal. Overridden in InheritableThreadLocal. Params: T -- The current thread Returns: the map **/
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}
Copy the code

Thread maintains the ThreadLocalMap variable:

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

To sum up, threadLocalMaps are written in ThreadLocal using inner classes, but object references are in Thread.

Thread maintains a Map of the ThreadLocalMap for each Thread. The key of the ThreadLocalMap is the ThreadLocal object itself, and the value is the object to store.

With that in mind, the get() method is not at all difficult to understand:

/** * gets the value of the entry under the current thread. * Fetch value */ from the map using the current ThreadLocal key 
public T get() { 
        // Get the current thread
        Thread t = Thread.currentThread(); 
        // Get the ThreadLocalMap object for the current thread.
        ThreadLocalMap map = getMap(t); 
        // If yes. Then the entry object under this ThreadLocalMap is obtained. If the entry is also obtained, the value corresponding to the entry can be directly obtained and returned.
        if(map ! =null) { 
            // Get the entry object under this ThreadLocalMap
            ThreadLocalMap.Entry e = map.getEntry(this); 
            // If entry is also obtained
            if(e ! =null) { 
                @SuppressWarnings("unchecked") 
                // The value corresponding to the entry is returned.
                T result = (T)e.value; 
                returnresult; }}// If ThreadLocalMap or Entry is not obtained, set the initial value.
        // The initial value method is lazy loading, which is only used in get, and is only initialized if the fetch fails
        return setInitialValue(); 
}
Copy the code

conclusion

  1. Each Thread maintains a reference to a ThreadLocalMap
  2. ThreadLocalMap is an internal class of ThreadLocal that uses Entry for storage
  3. When you call ThreadLocal’s set() method, you actually set a value to the ThreadLocalMap, where the key is the ThreadLocal object and the value is the object passed in
  4. When you call ThreadLocal’s get() method, you actually fetch the value from ThreadLocalMap, and the key is the ThreadLocal object
  5. ThreadLocal itself does not store values; it simply acts as a key for the thread to retrieve values from ThreadLocalMap.

So ThreadLocal can implement data isolation, fetching local variable values from the current thread, independent of other threads.

Are objects in ThreadLocal thread-safe?

If a threadlocale.set () enters a shared object in each thread, such as a static object, then threadlocale.get () will fetch the shared object itself, or it will be unsafe for concurrent access.

What about ThreadLocal memory leaks in thread pools?

The problem

First analysis:

  • ThreadLocalMap.EntryWill the key leak memory?
  • ThreadLocalMap.EntryDoes the value of the

Take a look at the core source code of key-value

static class Entry extends WeakReference<ThreadLocal<? >> { Object value; Entry(ThreadLocal<? > k, Object v) { super(k); value = v; }}Copy the code

The super(key) class is a weak reference, so there is no memory leak for the key because it is not a strong reference and can be collected by GC.

Weak reference characteristics: If the object is only associated with weak references and there is no strong reference association, then the object can be collected by GC. Weak references do not prevent GC collection. This is JVM knowledge.

Value is a strong reference, but it is not a problem because the thread terminates. Both strong and weak references are discarded by GC because the reference chain is broken.

There seems to be nothing wrong with this analysis, but what about thread pools? Once created, the core thread will be reused and its life cycle will not end. However, weak references to keys will be recycled by GC, and strong references to values will not be recycled, thus forming the following scene:

Thread - > ThreadLocalMap - > Entry (the key is null) - > value

Since value and Thread are still connected and reachable, they will not be recycled. As a result, more and more garbage objects are generated but cannot be recycled. Sooner or later, memory leaks will occur, and after a long time, they must be OOM.

So the solution ThreadLocal has already figured out for us, providing the remove() method, which removes the value. So remember remove() when you’re done.

Avoid Memory leaks

The root cause of a ThreadLocal memory leak is that since ThreadLocalMap has the same lifetime as Thread, memory leaks occur if the corresponding key is not manually removed, not because of weak references. To avoid memory leaks, remove() manually!

Usage scenarios of ThreadLocal

The most common uses of ThreadLocal are for database connections, Session management, and so on.

Database connection:

private static ThreadLocal<Connection> connectionHolder=new ThreadLocal<Connection>() {
    public Connection initialValue() {
        returnDriverManager.getConnection(DB_URL); }}; publicstatic Connection getConnection() {
    return connectionHolder.get();
}
Copy the code

Session management:

private static final ThreadLocal threadSession =new ThreadLocal();
 
public static Session getSession()throws InfrastructureException {
    Session s = (Session) threadSession.get();
    try {
        if (s ==null) { s = getSessionFactory().openSession(); threadSession.set(s); }}catch (HibernateException ex) {
        throw new InfrastructureException(ex);
    }
    return s;
}
Copy the code

The statement

Ps: I mixed a programmer, if there is an error in the article please inform us, in addition to this article only for learning, reference the following article please inform me to delete: ThreadLocal- Zhihu