ThreadLocal

1. Definition:

** Description in the Java official documentation: **
The ThreadLocal class is used to provide local variables within a thread. This ensures that variables in each thread are relatively independent of variables in other threads when they are accessed in a multi-threaded environment (via get and set methods). ThreadLocal instances are generally of private static type and are used to associate threads with their context.
The purpose of a ThreadLocal is to provide local variables within a thread, so that different threads do not interfere with each other. These variables work over the lifetime of the thread, reducing the complexity of passing some common variables between multiple functions or components within the same thread.Copy the code

A ThreadLocal is called a thread variable (local), and the populated data belongs only to the current thread. The variable is isolated from other threads. ThreadLocal creates a copy of a variable in each thread, so each thread can access its own internal copy variable. Synchronized is space for time, and Synchronized is time for space

ThreadLocal variable definition: a ThreadLocal variable.

Scope: Inside the thread

Life cycle: A variable’s life ends when the thread ends, along with thread execution

Shareability: Not shared between multiple threads

ThreadLocal solves the problem of passing variables within the same thread. ThreadLocal does not solve thread-safety problems in the first place, most obviously because the variables that a ThreadLocal holds internally are not shared between multiple threads. How can we be thread-safe when data is not shared? Of course, if the variables inside a ThreadLocal are themselves data shared by multiple threads, then there are thread-safety issues.

Without ThreadLocal, the data we need to share within the same thread is roughly passed through methods. This can make the code seem cluttered.

Conclusion:

  1. Thread concurrency: In a scenario where multiple threads are concurrent
  2. Pass data: We can pass public variables in different components on the same thread using ThreadLocal
  3. Thread isolation: Variables in each thread are independent and do not affect each other

The advantages and disadvantages:

Advantages:
  1. Pass data: Data is saved for each thread binding and can be retrieved directly where needed, avoiding code coupling problems caused by passing parameters directly
  2. Thread isolation: Data between threads is isolated and concurrent, avoiding the performance penalty of synchronization
Disadvantages:

1. Cannot pass between parent and child threads. Local thread variables set in the parent thread cannot be accessed in the child thread. To solve this problem, a new class InheritableThreadLocal was introduced.

2, easy to cause memory leaks

2, ThreadLocal method

Method statement describe
ThreadLocal() Create a ThreadLocal object
public void set( T value) Sets the local variable bound to the current thread
public T get() Gets the local variable bound to the current thread
public void remove() Removes a local variable bound to the current thread
protected T initialValue() { } InitialValue () is a protected method, usually intended to be overwritten when used. This method is a deferred method that executes only once the first time a thread calls get() or set(Object). The default implementation in ThreadLocal simply returns a null.

1. Get () method

 /** * returns the value of the ThreadLocal stored in the current thread. * If the current thread does not have the ThreadLocal variable, * it will return the value by calling {@link#initialValue} method to initialize the value * *@returnReturns the value */ of this ThreadLocal for the current thread
    public T get(a) {
        // Get the current thread object
        Thread t = Thread.currentThread();
        // Get the ThreadLocalMap object maintained in this thread object
        ThreadLocalMap map = getMap(t);
        // If this map exists
        if(map ! =null) {
            // Call getEntry to get the corresponding storage entity e with the current ThreadLocal as the key
            ThreadLocalMap.Entry e = map.getEntry(this);
            // Find the corresponding storage entity e
            if(e ! =null) {
                @SuppressWarnings("unchecked")
                // Obtain the value of storage entity e
                // That is the value we want for this ThreadLocal for the current thread
                T result = (T)e.value;
                returnresult; }}// If the map does not exist, then this thread has no maintained ThreadLocalMap object
        // Call setInitialValue to initialize
        return setInitialValue();
    }

    /** * variant implementation of set, used to initialize the value initialValue, * used instead to prevent users from overriding the set() method **@returnThe initial value Indicates the initial value */
    private T setInitialValue(a) {
        // Call initialValue to get the initialized value
        T value = initialValue();
        // Get the current thread object
        Thread t = Thread.currentThread();
        // Get the ThreadLocalMap object maintained in this thread object
        ThreadLocalMap map = getMap(t);
        // If this map exists
        if(map ! =null)
            // If there is, call map.set to set the entity entry
            map.set(this, value);
        else
            // 1) Thread does not have a ThreadLocalMap object
            // 2) Calls createMap to initialize the ThreadLocalMap object
            // 3) and store this entity entry as the first value in ThreadLocalMap
            createMap(t, value);
        // Return the set value
        return value;
    }

    /**
     * 获取当前线程Thread对应维护的ThreadLocalMap 
     * 
     * @paramT the current thread *@returnThe map corresponds to the maintained ThreadLocalMap */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    /** * Create ThreadLocalMap ** for current Thread@paramT Current thread *@paramFirstValue Specifies the value of the first entry stored in the map */
    void createMap(Thread t, T firstValue) {
        // Where this is the threadLocal that calls this method
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
Copy the code
Execute the process

A. First obtain the current thread

B. Obtain a Map based on the current thread

C. If the obtained Map is not empty, use the reference of ThreadLocal as the key to obtain the corresponding value e in the Map. Otherwise, go to E

D. If e is not null, return E. value, otherwise go to e

E. If Map is empty or E is empty, the initialValue is obtained by using the initialValue function, and then a new Map is created using the reference and value of ThreadLocal as firstKey and firstValue

Conclusion:

Get the ThreadLocalMap variable for the current thread, return a value if it exists, or create and return an initial value if it does not.

2. Set () method

  	/** * Sets the value of ThreadLocal for the current thread@paramValue Specifies the value to be stored in the current thread's corresponding ThreadLocal */
    public void set(T value) {
        // Get the current thread object
        Thread t = Thread.currentThread();
        // Get the ThreadLocalMap object maintained in this thread object
        ThreadLocalMap map = getMap(t);
        // Check whether the map exists
        if(map ! =null)
            // If there is, call map.set to set the entity entry
            map.set(this, value);
        else
            // 1) Thread does not have a ThreadLocalMap object
            // 2) Calls createMap to initialize the ThreadLocalMap object
            // 3) and store t(the current thread) and value(the corresponding value of t) as the first entry in the ThreadLocalMap
            createMap(t, value);
    }

	 /**
     * 获取当前线程Thread对应维护的ThreadLocalMap 
     * 
     * @paramT the current thread *@returnThe map corresponds to the maintained ThreadLocalMap */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

	/** * Create ThreadLocalMap ** for current Thread@paramT Current thread *@paramFirstValue Specifies the value of the first entry stored in the map */
	void createMap(Thread t, T firstValue) {
        // Where this is the threadLocal that calls this method
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

Copy the code
Execute the process

A. First obtain the current thread and obtain A Map based on the current thread

B. If the obtained Map is not empty, set the parameter to the Map (the reference to the current ThreadLocal is the key)

C. If Map is empty, a Map is created for the thread and an initial value is set

3. Remove () method

 	/** * Delete the entry */ entity that corresponds to ThreadLocal stored in the current thread
     public void remove(a) {
        // Get the ThreadLocalMap object maintained in the current thread object
         ThreadLocalMap m = getMap(Thread.currentThread());
        // If this map exists
         if(m ! =null)
            // If it exists, call map.remove
            // Delete the corresponding entity entry with the current ThreadLocal as the key
             m.remove(this);
     }
Copy the code
Execute the process

A. First obtain the current thread and obtain A Map based on the current thread

B. If the obtained Map is not empty, remove the entry corresponding to the current ThreadLocal object

4,The initialValue method

* The first call to this method occurs when a thread passes the {@linkThe ThreadLocal value of this thread is accessed by the ThreadLocal method * unless the thread first calls {get}@link#set} method, in this case, * {@codeInitialValue} is not called by this thread. * Normally, this method is called at most once per thread. * * <p> This method simply returns null {@codenull}; * If the programmer wants ThreadLocal to have an initial value other than null, * must pass the {@codeIn general, you can override this method by using an anonymous inner class@returnThe initial value of the current ThreadLocal */
protected T initialValue(a) {
    return null;
}
Copy the code

Return the initial value of a thread local variable

(1) This method is a delayed call method. From the above code, we know that it is executed only once when the set method is called before the get method is called.

(2) The default implementation of this method returns null directly.

(3) Override this method if you want an initial value other than null. (Note: this method is protected and is obviously designed to be overridden by subclasses)

3. The ThreadLocal core structure

Early design:

Each ThreadLocal class creates a Map, then uses the threadID threadID as the Map’s key and the local variable to be stored as the Map’s value, thus isolating the local variables of each thread. This is the simplest design approach

Design after JDK8:

The JDK8 ThreadLocal design is that each Thread maintains a hash table called ThreadLocalMap. The key of the hash table is the ThreadLocal instance itself, and the value is the actual value Object to store.

(1) Each Thread has an internal Map (ThreadLocalMap)

(2) The Map contains copies of ThreadLocal objects (keys) and thread variables (values)

(3) The Map inside the Thread is maintained by the ThreadLocal, which is responsible for obtaining and setting the variable values of the Thread from the Map.

(4) For different threads, when obtaining the copy value each time, other threads cannot obtain the copy value of the current thread, forming the isolation of copies and mutual interference.

Advantages:

1) After this design, the number of entries stored per Map will be smaller, because the number of entries stored was previously determined by the number of Threads, and is now determined by the number of ThreadLocal. (Not every thread needs to create a ThreadLocal, depending on your requirements)

2) When the Thread is destroyed, the corresponding ThreadLocalMap is also destroyed, reducing memory usage.

4, ThreadLocalMap

ThreadLocalMap is an inner class of ThreadLocal. It does not implement the Map interface. It implements the Map function in a separate way, and its internal Entry is also implemented independently.

The main difference between ThreadLocalMap and HashMap is that the structure of ThreadLocalMap is very simple and there is no next reference, which means that ThreadLocalMap resolves Hash collisions in a linear probe rather than a linked list.

Linear detection

1. Basic institutions

1. Member variables
    /** * Initial capacity -- must be a full power of 2 */
    private static final int INITIAL_CAPACITY = 16;

    /** * The table and Entry classes are defined below * Also, the array length must be a full power of 2. * /
    private Entry[] table;

    /** * Specifies the number of entrys in the array, which can be used to determine whether the current table usage exceeds the threshold. * /
    private int size = 0;

    /** * Specifies the threshold for expansion. If the table usage exceeds the threshold, expansion is performed. * /
    private int threshold; // Default to 0
    
Copy the code

Brief description:

Similar to HashMap, INITIAL_CAPACITY indicates the initial capacity of the Map; A table is an array of entries used to store data. Size represents the number of stores in the table; Threshold indicates the size threshold for capacity expansion.

2. Storage structure – Entry

In ThreadLocalMap, k-V structure data is also stored with entries. However, the key in an Entry can only be a ThreadLocal object, which is restricted by the Entry constructor.

/* * Entry inherits WeakReference(** WeakReference **) and uses ThreadLocal as key. * if the key is null(entry.get() == null), it means that the key is no longer referenced. * So the entry can also be removed from the table. * /
static class Entry extends WeakReference<ThreadLocal> {
    /** The variable bound to this ThreadLocal */
    Object value;

    Entry(ThreadLocal k, Object v) {
        // ThreadLocal is a weak reference
        super(k); value = v; }}Copy the code

Note: Entry inherits from WeakReference (WeakReference whose life cycle can only survive until the next GC), but only Key is a WeakReference type, and Value is not a WeakReference.

Confused:

A memory leak occurs when using a ThreadLocal, which has nothing to do with the Entry using a weak reference key.

Because the key of ThreadLocalMap is a weak reference, and the Value is a strong reference. This leads to a problem where ThreadLocal does not have a strong reference to an external object. When GC occurs, the weak reference Key is reclaimed, but the Value is not.

When a thread does not end, but ThreadLocal has been recollected, the thread may have a ThreadLocalMap< NULL, Object> key-value pair, causing memory leaks. (The ThreadLocal is reclaimed, but the ThreadLocal’s associated thread-shared variable still exists) causing a memory leak

2.Memory leaks and weak references

Memory leak:

Memory overflow:

There is not enough memory for the applicant. Memory leak:

Refers to the program has been dynamically allocated heap memory for some reason the program is not released or can not be released, resulting in a waste of system memory, resulting in program running speed slow down or even system crash and other serious consequences. The accumulation of memory leaks will eventually lead to a memory overflow.

Weak references:

There are four types of references in Java: strong, soft, weak, and virtual. The current problem involves strong and weak references:

A “Strong” Reference is a common Reference to an object. As long as there is a Strong Reference to an object, the object is “alive” and the garbage collector does not reclaim it.

WeakReference: once the garbage collector finds an object with only WeakReference, it will reclaim its memory regardless of whether the current memory space is sufficient or not.

Java about the difference and usage of strong references, soft references, weak references and virtual references

JVM is like a country, GC is the chengguan, strong reference is the local people, soft reference is the immigrants, weak reference is the black hukou, which day the chengguan caught and sent away, false reference is a sick black hukou, which day you hang up

1. If the key is strongly referenced

Given that the key in ThreadLocalMap uses a strong reference, is there a memory leak?

The memory graph of the ThreadLocal (solid lines represent strong references) is as follows:

Assuming that ThreadLocal is used up in the business code, the ThreadLocal Ref (current ThreadLocal) is reclaimed. 'But because the Entry of a threadLocalMap strongly references a threadLocal, the threadLocal cannot be reclaimed. ThreadRef -> CurrentThread ->threadLocalMap-> Entry The Entry is not reclaimed (the Entry contains ThreadLocal instances and values), resulting in Entry memory leaks. That is, the key in ThreadLocalMap uses a strong reference, and memory leaks cannot be completely avoided.Copy the code
2. If the key is a weak reference

So is there a memory leak because the key in ThreadLocalMap uses a weak reference?

The memory graph of the ThreadLocal (solid lines for strong references, dotted lines for weak references) is as follows:

'Also assume that when ThreadLocal is used up in the business code, the ThreadLocal Ref (current ThreadLocal) is reclaimed. Since the ThreadLocalMap only holds weak references to the ThreadLocal and does not have any strong references to the ThreadLocal instance, the ThreadLocal can be successfully collected by GC with the key in the Entry =null. ThreadRef -> CurrentThread ->threadLocalMap-> Entry -> value; This value will never be accessed, resulting in a value memory leak. That is, the key in ThreadLocalMap uses weak references, and memory leaks are possible.Copy the code

3. The actual cause of memory leaks in ThreadLocal

Comparing the two cases, we can see that the memory leak occurs regardless of whether the key in ThreadLocalMap uses weak references. So what is the real cause of memory leaks?

In both cases of memory leaks, there are two premises:

  1. This Entry was not manually deleted
  2. CurrentThread is still running

The first point is easy to understand. You can avoid memory leaks by simply calling the remove() method of ThreadLocal to remove the corresponding Entry after using it.

The second point is a little more complicated. Since ThreadLocalMap is a property of Thread and is referenced by the current Thread, it has the same lifetime as Thread. After using ThreadLocal, if the current Thread is executed, the ThreadLocalMap will be collected by GC, preventing memory leaks at the root.

The root cause of a ThreadLocal memory leak is:

Because a ThreadLocalMap has the same lifetime as a Thread (each Thread maintains a ThreadLocalMap hash table), failure to manually remove the corresponding key would result in a memory leak.

4. Why use weak references

From the above analysis, no matter what type of reference the key in ThreadLocalMap uses, memory leaks are completely avoided, regardless of the use of weak references.

There are two ways to avoid memory leaks:

  1. After using a ThreadLocal, call its remove method to remove the corresponding Entry
  2. When ThreadLocal is used, the current Thread runs out

The second approach is obviously less manageable than the first, especially when using thread pools, where thread terminations are not destroyed.

That is, as long as you remember to call Remove immediately after using ThreadLocal, it doesn’t matter whether the key is a strong or weak reference. So why use weak references for keys?

In fact, the set/getEntry method in a ThreadLocalMap determines that the key (and therefore the ThreadLocal) is null, and if it is null, then the value is set to null.

This means that if the CurrentThread is still running after ThreadLocal is used, even if you forget to call remove, a weak reference is more secure than a strong reference: Weak references to a ThreadLocal are recovered, and the corresponding value is removed the next time ThreadLocalMap calls any of the set,get, or remove methods (it is not removed until the next call to any of the set,get, or remove methods). Thus avoiding memory leaks.

5.Hash conflict resolution

Hash conflict resolution is an important part of Map. Let’s take a look at the core source code of ThreadLocalMap using hash conflict resolution as a clue.

(1) Start with the set() method of ThreadLocal

  public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocal.ThreadLocalMap map = getMap(t);
        if(map ! =null)
            // The set method of ThreadLocalMap is called
            map.set(this, value);
        else
            createMap(t, value);
    }
    
    ThreadLocal.ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

    void createMap(Thread t, T firstValue) {
        	// The constructor of ThreadLocalMap is called
        t.threadLocals = new ThreadLocal.ThreadLocalMap(this, firstValue);
    }

Copy the code

This method, which was analyzed above (2.2), sets the local variable bound to the current thread:

  1. First get the current thread and get a Map based on the current thread
  2. If the obtained Map is not empty, the parameter is set to the Map (the reference to the current ThreadLocal is the key)

(The set method of ThreadLocalMap is called here.)

  1. If Map is empty, a Map is created for the thread and an initial value is set

(The constructor of ThreadLocalMap is called here)

There are two parts of this code that involve ThreadLocalMap’s two methods, which we then examine.

(2) ThreadLocalMap(ThreadLocal<? > firstKey, Object firstValue)

 /* * firstKey: this ThreadLocal instance (this) * firstValue: the ThreadLocal variable to hold */ThreadLocalMap(ThreadLocal<? > firstKey, Object firstValue) {// Initialize the table
        table = new ThreadLocal.ThreadLocalMap.Entry[INITIAL_CAPACITY];
        // Calculate index (key code)
        int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
        / / set the value
        table[i] = new ThreadLocal.ThreadLocalMap.Entry(firstKey, firstValue);
        size = 1;
        // Set the threshold
        setThreshold(INITIAL_CAPACITY);
    }

Copy the code
The 'constructor creates an array of 16 entries, calculates the index for firstKey, stores it in the table, and sets the size and threshold.Copy the code

The key analysis: int I = firstKey. ThreadLocalHashCode & (INITIAL_CAPACITY – 1).

1. About firstKey. ThreadLocalHashCode:

 	private final int threadLocalHashCode = nextHashCode();
    
    private static int nextHashCode(a) {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }
	
	AtomicInteger is an Integer class that provides atomic operations, such as addition and subtraction in a thread-safe manner
    private static AtomicInteger nextHashCode =  new AtomicInteger();
     /** Special hash value difference between continuously generated hash codes - converts the implicit sequential thread local ID to a multiplications hash value close to the optimal distribution for tables of size 2. * /
    private static final int HASH_INCREMENT = 0x61c88647;

Copy the code

Here we define an AtomicInteger that takes the current value every time and adds HASH_INCREMENT, HASH_INCREMENT = 0x61C88647, which is related to the Fibonacci sequence, The main purpose is to make the hash code evenly distributed in the 2n-power array, namely the Entry[] table, so as to avoid hash collisions as much as possible.

Atomic classes (Java. Util. Concurrent. Atomic package) under the mechanism of CAS

CAS: Compare And Swap; A special instruction that operates on shared data in memory, widely supported by modern cpus. CAS can convert read-modify-write to an atomic operation, which is guaranteed directly by the processor. (Classic implementation: ConcurrentHashMap)

The CAS mechanism uses three basic operands: memory address V, the old expected value A, and the new value B to be modified.

2. About & (INITIAL_CAPACITY – 1)

HashCode & (size – 1) is used to compute the hash, which is a more efficient implementation of modulo hashCode % size. Because of this algorithm, we require the size to be a full power of 2, which also ensures that the number of hash collisions is reduced without the index crossing the boundary.

(3) Set method in ThreadLocalMap

private void set(ThreadLocal
        key, Object value) {
        ThreadLocal.ThreadLocalMap.Entry[] tab = table;
        int len = tab.length;
        // Calculate the index (key code, analyzed above)
        int i = key.threadLocalHashCode & (len-1);
        /** * Use linear probe to find elements (key code) */
        for(ThreadLocal.ThreadLocalMap.Entry e = tab[i]; e ! =null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<? > k = e.get();// The key corresponding to ThreadLocal exists, overriding the previous value directly
            if (k == key) {
                e.value = value;
                return;
            }
            // If the key is null, but the value is not null, the previous ThreadLocal object has been reclaimed.
           // The Entry in the current array is a stale element
            if (k == null) {
                // Replace old elements with new ones. This method does a lot of garbage cleaning to prevent memory leaks
                replaceStaleEntry(key, value, i);
                return; }}// If the ThreadLocal key does not exist and no stale element is found, a new Entry is created at the empty element location.
            tab[i] = new Entry(key, value);
            int sz = ++size;
            / * * * cleanSomeSlots used to remove those um participant et () = = null elements, * the data key associated objects have been recovered, so the Entry (table [index]) can be null. * If no entry is cleared and the current usage reaches the threshold [load factor defined (2/3 of the length)], then * rehash (perform a sweep of the whole table) */
            if(! cleanSomeSlots(i, sz) && sz >= threshold)/** * Repackage and/or resize the table. Start by scanning the entire table to remove stale entries. * If this does not reduce the table size sufficiently, double the table size. * /
                rehash();
}

 /** * Get the next index of the ring array */
    private static int nextIndex(int i, int len) {
        return ((i + 1 < len) ? i + 1 : 0);
    }


Copy the code

Code execution process:

  1. First, calculate the index I based on the key, and then look for the Entry at the position I
  2. If the Entry already exists and the key is equal to the key passed in, assign a new value to the Entry.
  3. If the Entry exists but the key is null, then call replaceStaleEntry to replace the Entry with an empty key. (Replace the old element with a new one. This method does a lot of garbage cleaning to prevent memory leaks.)
  4. Keep iterating until it reaches a null position. If it does not return during the loop, create a new Entry at the null position and insert it, increasing the size by 1.
  5. Finally, call cleanSomeSlots to clean up the Entry with null key. Finally, return whether the Entry has been cleaned up. Then determine whether sZ >= Thresgold meets the condition of rehash.

Key analysis: ThreadLocalMap uses linear detection to resolve hash collisions.

This method probes the next address at a time until the address is empty, and then inserts it. If the whole space cannot find the empty address, the overflow occurs.Copy the code

For example, if the current table length is 16, that is, if the calculated hash value of the key is 14, if table[14] already has a value, and its key is inconsistent with the current key, then a hash conflict occurs. In this case, add 14 by 1 to get 15, and take table[15] for judgment. If there is still a conflict, it will go back to 0, get table[0], and so on until it can be inserted.

As described above, you can think of Entry[] table as a circular array.

5. The ThreadLocal class and synchronized keyword

public class TestThreadLocal {
    private String content;
    / / create a ThreadLocal
    ThreadLocal<String> stringLocal = new ThreadLocal<String>();

    public String getContent(a) {
        return content;
        // Get the current thread variable
		  //return stringLocal.get();
    }

    public void setContent(String content) {
        this.content = content;
        // Thread binding variables
		//stringLocal.set(content);
    }

    public static void main(String[] args) {
        TestThreadLocal testThreadLocal = new TestThreadLocal();
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run(a) {
                    synchronized (TestThreadLocal.class) {
                        testThreadLocal.setContent(Thread.currentThread().getName() + "Data");
                        System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
                        System.out.println(Thread.currentThread().getName() + "-- -- -- -- >"+ testThreadLocal.getContent()); }}}); thread.setName("Thread"+ i); thread.start(); }}}Copy the code

The difference between:

Synchronized is used for data sharing between threads, while ThreadLocal is used for data isolation between threads.

synchronized ThreadLocal
The principle of The synchronization mechanism uses a time-for-space approach,Only one copy of the variable is provided for different threads to queue access ThreadLocal takes the ‘space for time’ approach,Each thread is provided with a copy of the variableIn order to achieve simultaneous access without interference
focus Synchronization of access to resources between multiple threads In multithreading, the data of each thread is isolated from each other

Conclusion:

Using ThreadLocal is more appropriate because it allows for higher concurrency in the program.

6, not

public class TestThreadLocal { private String content; ThreadLocal<String> stringLocal = new ThreadLocal<String>(); public String getContent() { return content; // return stringLocal.get(); } public void setContent(String content) { this.content = content; // thread binding variable // stringLocal.set(content); } public static void main(String[] args) { TestThreadLocal testThreadLocal = new TestThreadLocal(); for (int i = 0; i < 5; i++) { Thread thread = new Thread(new Runnable() { @Override public void run() { TestThreadLocal. SetContent (Thread. CurrentThread (). The getName () + "data"); System.out.println("-----------------------"); System.out.println(Thread.currentThread().getName() + "---->" + testThreadLocal.getContent()); }}); Thread.setname (" thread "+ I); thread.start(); }}}Copy the code

7, problem,

Do memory leaks have anything to do with weak references to ThreadLocalMap keys? What is the real cause of memory leaks?

It doesn’t matter.

** The real reason: ** Since ThreadLocalMap has the same lifetime as Thread, failing to manually delete the corresponding key would cause a memory leak

2. How to avoid leakage

1, after using the thread shared variable, the display calls threadLocalMap. remove method to clear the thread shared variable; Since the Key is a weak reference, all we need to do is to call the Remove method after the get() and set() methods of ThreadLocal have been called to remove the reference relationship between the Entry node and the Map, so that the entire Entry object becomes unreachable after GC Roots analysis. It can be collected in the next GC.

2. The JDK recommends that ThreadLocal be private static to avoid repeated instances. This way, weak references to ThreadLocal do not exist. (The purpose of the ThreadLocal class is to maintain the value of a variable separately for each thread, avoiding competing access to the same variable between threads, and for situations where a variable needs to have its own value in each thread.)

Why the ThreadLocal variable is usually set to static

ThreadLocal is just like locking to unlock; when used, you need to clean up.

3. So why use weak references for keys?

In fact, the set/getEntry method in a ThreadLocalMap determines that the key is null (i.e., the ThreadLocal is null), and nuL sets the value to null.

This means that after using ThreadLocal, As long as CurrentThread is still running, even if you forget to call remove, a weak reference has one more layer of guarantee than a strong reference: the weak reference’s ThreadLocal will be recovered, and the corresponding value will be called the next time the ThreadLocalMap calls any of the set,get, or remove methods To avoid memory leaks.

reference

  1. The Dark Horse Programmer Java Basics tutorial takes a comprehensive look at ThreadLocal from the ground up
  2. Parse ThreadLocal from the bottom up