ThreadLocal

Preface:

In the Thread class there is a field:

ThreadLocal.ThreadLocalMap threadLocals = null;

This field belongs to the current thread and is not passed during thread switching. In fact, ThreadLocal operates on this parameter and acts as a thread isolation. . In essence, ThreadLocal ThreadLOcalMap can be understood as the Thread Thread object inside a private field, will not be subclasses or other object access to

Meaning:

This is outside of the thread lock to make sure that these variables are not referenced or isolated from each other

The source code parsing

get

/** * returns the value in the current thread copy of this thread-local variable. If the variable does not have a value for the current thread, it is first initialized to the value returned by calling the initialValue method. Return: * The value of the current thread local to this thread **/
public T get(a) {
    // Get the current thread
    Thread t = Thread.currentThread();
    // Get the Map of the current thread
    ThreadLocalMap map = getMap(t);
    // Check whether there is a map
    if(map ! =null) {
       // Get the value from this (current thread as key) if the current MAO exists
        ThreadLocalMap.Entry e = map.getEntry(this);
        if(e ! =null) {
            // If Entry is not null (null if not set)
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            returnresult; }}// Set was not called before get was called
    return setInitialValue();
}
Copy the code

set

/** * Sets the current thread copy of this thread-local variable to the specified value. Most subclasses do not need to override this method, * relying only on the initialValue method to set the value of a thread-local variable. * Argument: *value - The value to be stored in the current thread copy local to this thread. * * /
public void set(T value) {
   // Get the current thread
    Thread t = Thread.currentThread();
   // Get the current thread map
    ThreadLocalMap map = getMap(t);
   // Check whether it exists. If so, set it directly
    if(map ! =null) {
        map.set(this, value);
    } else {
      // If no map is initializedcreateMap(t, value); }}Copy the code

createMap

void createMap(Thread t, T firstValue) {
   // Create a ThreadLocalMap object, or initialize it
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}
Copy the code

getMap

/ / the method directly pointing at the Thread ThreadLocal. ThreadLocalMap threadLocals = null; On this field

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}
Copy the code
/** * Variant of set() to establish initialValue. Used instead * of set() in case user has overridden the set() method. * * @return the initial value */ private T setInitialValue() {// This parameter defaults to null. Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map ! Map.set (this, value); // Set map.set(this, value); } else {// If not then create a createMap(t, value); } // If the thread is an instance of this object, If (this instanceof TerminatingThreadLocal) { TerminatingThreadLocal.register((TerminatingThreadLocal<? >) this); } // Return the default value. }Copy the code

remove

Calling the GET method after the current call may result in multiple calls to the initialized method

public void remove(a) {
// Get the map parameter of ThreadLocal
    ThreadLocalMap m = getMap(Thread.currentThread());
    if(m ! =null) {
       / / remove
        m.remove(this); }}Copy the code

Note: Memory leak problem

We know that Thread has a maplocal in it so this parameter is an inner class in ThreadLocal which is actually an array of entries, but this array is an array of weak application types. We know that if the reference type, when the GC occurs, the object will be reclaimed by the GC if there is only one weak reference to the object, and there is also a problem if the map object of ThreadLocal is not reclaimed by other strong references, causing a memory leak.

The Entry class inherits the weakly referenced class, and the constructor uses the parent class directly, so this is a weakly referenced class;

static class Entry extends WeakReference<ThreadLocal<? >>{
    // This class is the source of the memory leakEntry(ThreadLocal<? > k, Object v) {super(k); value = v; }}Copy the code

InheritableThreadLocal

Preface:

We can isolate a method that interacts with data between threads by looking at ThreadLocal, which is the InheritableThread class that we want to use to interact with data between threads.

ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; This class is essentially the same parameter as ThreadLocal, with different controls to implement one that is accessible to subclasses and one that is not

Source:

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    // This method works with packages
    protected T childValue(T parentValue) {
        return parentValue;
    }
​
    // Inherits from the parent method - the difference is that the reference object is different
    ThreadLocalMap getMap(Thread t) {
       returnt.inheritableThreadLocals; } /------------------ parent method implementationThreadLocalMap getMap(Thread t) {
        returnt.threadLocals; } / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --// Also inherits from the parent class, the difference is the same as getMap
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue); }}Copy the code

setInitialValue

Initialize value-submethods that have getMap methods inside, ThreadLocal has its own implementation, which refers to its own Map, and subclasses, [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] Or the initialized object is different.

private T setInitialValue(a) {
   // Call the initialization method
    T value = initialValue();
    // Get the current thread
    Thread t = Thread.currentThread();
    // Get the map parameter of the current thread ThreadLocal
    ThreadLocalMap map = getMap(t);
    // Initialize as required
    if(map ! =null) {
        map.set(this, value);
    } else {
        createMap(t, value);
    }
    if (this instanceofTerminatingThreadLocal) { TerminatingThreadLocal.register((TerminatingThreadLocal<? >)this);
    }
    return value;
}
Copy the code

init

When creating a thread, initialize the inheritThreadLocals field, which is a Boolean value that inherits variables from the parent class, and the map field that stores data from the parent class. [inheritableThreadLocal] [getMap] [inheritableThreadLocal] [getMap] [inheritableThreadLocal] [getMap] [inheritableThreadLocal] It points directly to the Inheritable so we can access the parent variables at this point.

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }
​
    this.name = name;
​
    Thread parent = currentThread();
    SecurityManager security = System.getSecurityManager();
    if (g == null) {
 
        if(security ! =null) {
            g = security.getThreadGroup();
        }
​
  
        if (g == null) {
            g = parent.getThreadGroup();
        }
    }
​

    g.checkAccess();
​
    if(security ! =null) {
        if (isCCLOverridden(getClass())) {
            security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
        }
    }
​
    g.addUnstarted();
​
    this.group = g;
    this.daemon = parent.isDaemon();
    this.priority = parent.getPriority();
    if (security == null || isCCLOverridden(parent.getClass()))
        this.contextClassLoader = parent.getContextClassLoader();
    else
        this.contextClassLoader = parent.contextClassLoader;
    this.inheritedAccessControlContext = acc ! =null ? acc : AccessController.getContext();
    this.target = target;
    setPriority(priority);
    // This is the key step in providing field access to subclasses
    if(inheritThreadLocals && parent.inheritableThreadLocals ! =null)
        this.inheritableThreadLocals =
            ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    /* Stash the specified stack size in case the VM cares */
    this.stackSize = stackSize;
​
    /* Set thread ID */
    tid = nextThreadID();
}
Copy the code

set

This method is reposted, and from the set method, we can conclude that subclasses can only inherit this variable, but they can’t continue to affect the parent variable.

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