If you are still in the business development stage of add, delete, change, and check, and are new to multi-threading, we will talk about an important class called ThreadLocal. ThreadLocal, commonly known as a local Thread, allows variables to be stored in a Thread.

Public class ThreadLocalExample implements Runnable{

private static final ThreadLocal<SimpleDateFormat> threadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd HHmm")); @Override public void run() { System.out.println("Thread Name= " + Thread.currentThread().getName() + " default Formatter = " + threadLocal.get().toPattern()); try { Thread.sleep(new Random().nextInt(1000)); } catch (InterruptedException e) { e.printStackTrace(); } threadLocal.set(new SimpleDateFormat()); System.out.println("Thread Name= " + Thread.currentThread().getName() + " Formatter = " + threadLocal.get().toPattern()); } public static void main(String[] args) throws InterruptedException { ThreadLocalExample threadLocalExample = new ThreadLocalExample(); for (int i = 0; i < 10; i++) { Thread thread = new Thread(threadLocalExample, "" + i); Thread.sleep(new Random().nextInt(1000)); thread.start(); }}Copy the code

} No matter how many threads there are, each thread has two results. A date format that the result sets for itself, and a default date format.

The running results are as follows:

Thread Name= 0 default Formatter = yyyyMMdd HHmm Thread Name= 0 Formatter = yy-M-d ah:mm Thread Name= 1 default Formatter = yyyyMMdd HHmm Thread Name= 2 default Formatter = yyyyMMdd HHmm Thread Name= 1 Formatter = yy-M-d ah:mm Thread Name= 2 Formatter = yy-M-d ah:mm Thread Name= 3 default Formatter = yyyyMMdd HHmm Thread Name= 3 Formatter = yy-M-d ah:mm Thread Name= 4 default Formatter = yyyyMMdd HHmm Thread Name= 6 default Formatter = yyyyMMdd HHmm Thread Name= 5 default Formatter = yyyyMMdd HHmm Thread Name= 4 Formatter = yy-M-d ah:mm Thread Name= 6 Formatter = yy-M-d ah:mm Thread Name= 5 Formatter = yy-M-d ah:mm Thread Name= 7 default Formatter = yyyyMMdd HHmm Thread Name= 8 default Formatter = yyyyMMdd HHmm Thread Name= 9 default Formatter = yyyyMMdd HHmm Thread Name= 7 Formatter = yy-M-d ah:mm Thread Name= 8 Formatter = YY-M-D AH :mm Thread Name= 9 Formatter = Yy-M-d AH :mm

❝ If you have a variable that you want to bind to a thread, you can implement it using ThreadLocal.

❞ Basic principles above said variables saved to the thread, how to save? Let’s look at the source of the Thread class:

public class Thread implements Runnable { …

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

. } find a ThreadLocalMap, so I wonder if the variable is placed in the value of the Map. Looking at the ThreadLocalMap code we see that:

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

} ThreadLocalMap is actually an Entry whose “key” is a ThreadLocal and “value” is the value we store in it.

WeakReference<ThreadLocal<? > >. Is really a face of mengbi, WeakReference is what, found himself in the road of ignorance further and further. In fact, there is an explanation above the source code:

❝ 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.

❞ means that the “key” in the Entry is a weak reference. What weak references are we’ll talk about later.

Let’s look at ThreadLocal’s set method:

public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map ! = null) map.set(this, value); else createMap(t, value); } If ThreadLocalMap is available, store the value to ThreadLocalMap; otherwise, create a new ThreadLocalMap and store the value.

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); } get method is relatively simple, according to the “key” value.

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(); } The “key” of a ThreadLocalMap is a weak reference to a ThreadLocal. So what is a weak reference?

Let’s take a look at wikipedia:

❝ In computer programming, a “weak reference”, as opposed to a strong reference, is a reference that cannot be guaranteed that the object it refers to will not be collected by the garbage collector. An object that is referred to only by weak references is considered inaccessible (or weakly accessible) and can therefore be reclaimed at any time. Languages with garbage collection mechanisms, such as Java, C#, Python, Perl, Lisp, and so on, all support weak references to varying degrees.

❞ During garbage collection, the collector reclaims these weak references. Here’s an example of weak reference code:

import java.lang.ref.WeakReference;

public class ReferenceTest { public static void main(String[] args) throws InterruptedException {

WeakReference r = new WeakReference(new String("I'm here")); WeakReference sr = new WeakReference("I'm here"); System.out.println("before gc: r=" + r.get() + ", static=" + sr.get()); System.gc(); Thread.sleep(100); / / r. gutierrez et () into a null System. Only the out the println (" after gc: r = "+ r. gutierrez et () +", the static = "+ sr., the get ());Copy the code

}} The result is as follows:

Before GC: r=I’m here, static=I’m here after GC: r=null, static=I’m here

❝ So why does this “Entry” use a weak reference?

❞ If the Entry key uses a strong reference, the reference to the key will always point to the ThreadLocal object. If the Thread exists, the Entry will always exist, causing the risk of memory leakage.

But even with weak references there is a risk of memory leaks. When ThreadLocal is reclaimed, the key value becomes null, causing the entire value to be unreachable. There are still memory leaks, but there is one more layer of protection than strong references.

So how do we solve memory leaks?

The structure diagram of ThreadLocal is as follows:

When ThreadLocal is reclaimed, any of the set, get, or remove methods of the corresponding value will be cleared the next time ThreadLocalMap calls it. To avoid memory leaks. So when you run out of ThreadLocal, be careful to call the remove method.