introduce
ThreadLocal is a java.lang class that has been available since JDK1.2 and is very important in interviews and projects. The main purpose of this class is to provide thread-local variables, so it is often referred to as thread-local variables
Literally, this class creates a local variable for each thread. ThreadLocal actually creates a copy of the variable for each thread, giving each thread access to its own internal copy of the variable
Mentioned multithreading, usually consider variable synchronization problem, but a ThreadLocal is not in order to solve the problem of synchronization multi-threaded Shared variables, but in order to let each thread variables do not affect each other, the equivalent of manipulation is a copy of the variable between threads, natural need not consider multithreaded competition, and natural no performance loss
use
Let’s start with a couple of common methods
public T get(a) {}public void set(T value) {}public void remove(a) {}protected T initialValue(a) {}Copy the code
Obviously, the get() method gets the copy value owned by the thread, the set() method sets the value, the remove() method removes the value, and the initialValue() method initializes the variable. Let’s take a look at the following example and see the application scenario
public class Demo {
public static ThreadLocal<Integer> threadLocal = null;
public static void main(String[] args) {
threadLocal = new ThreadLocal<Integer>() {
/** * Initialize the value of ThreadLocal */ by overriding this method
@Override
protected Integer initialValue(a) {
return 10; }}; MyThread t1 =new MyThread(20);
MyThread t2 = new MyThread(30);
t1.start();
// The try-catch statement is omitted for clarityt1.join(); t2.start(); }}Copy the code
In the above method, we define and initialize a ThreadLocal class to 10 (implemented by overriding the initialValue() method), then start two threads, and let t2 wait for T1 to finish executing
The MyThread class details are as follows
class MyThread extends Thread {
private int val = 0;
MyThread(int val) {
this.val = val;
}
@Override
public void run(a) {
System.out.println(Thread.currentThread() + "-BEFORE-" + Demo.threadLocal.get());
Demo.threadLocal.set(val);
System.out.println(Thread.currentThread() + "-AFTER-"+ Demo.threadLocal.get()); }}Copy the code
We get the current value by calling the Get () method of the ThreadLocal object, then setting a new value by the set() method (we set different values for each thread), and then getting the set value by the get() method
The result is as follows
Also, if a ThreadLocal object is to be reused in many places, local variables need to be restored to default values by calling the **remove()** method before use
In theory, of course, this is possible, but ThreadLocal is far more convenient than private variables. It not only provides uniform initialization outside the thread, but also avoids setting additional variables inside the thread
The principle of
ThreadLocal is not responsible for storing variables, so we have to start with methods
Let’s start with the initial() method, which is where our variable’s default initial value is set, as follows
protected T initialValue(a) {
return null;
}
Copy the code
We override this method every time we create a ThreadLocal. Where does this method get() call
public T get(a) {
// Get the current thread
Thread t = Thread.currentThread();
// Get the map of the current thread
ThreadLocalMap map = getMap(t);
if(map ! =null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if(e ! =null) {
T result = (T)e.value;
returnresult; }}// Create map if map is empty
return setInitialValue();
}
Copy the code
A bit frowningly, we noticed that we had a ThreadLocalMap object, so we thought it would be possible to create a KV table for threads and variables, so that each thread could have its own variables
The getMap(t) method returns a threadLocals property for Thread T, which is a field of the Thread class:
ThreadLocal.ThreadLocalMap threadLocals = null;
Copy the code
This is a property maintained by the ThreadLocal class. None of Thread’s methods modify this field, and ThreadLocalMap itself is an inner class of ThreadLocal, which can be understood as a Map (although this class does not inherit the Map interface).
Also note that the Entry object (key-value pair) in the ThreadLocalMap object inherits a weak reference to ThreadLocaMap, as follows
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
That is, when ThreadLocal is left empty, the Key in the Entry will be reclaimed in the next YGC
We still don’t see the initialValue() method, so hold your fire and click on the setInitialValue() method, which is called if the map is null is detected in the GET () method, as follows
private T setInitialValue(a) {
// The initial value we set
T value = initialValue();
// The current thread
Thread t = Thread.currentThread();
// Check again to see if it is null
ThreadLocalMap map = getMap(t);
if(map ! =null)
map.set(this, value);
else
createMap(t, value);
return value;
}
Copy the code
The set() method and initialValue() method are almost exactly the same. The remove() method normally removes a KV key pair (K is the current thread), which is not listed here. If you are interested, you can check it by yourself
Matters needing attention
Dirty data
ThreadLocal is bound to a Thread, and each Thread has a value. If you do not call remove() after use, you will read dirty data on the next reuse (for the same Thread). In particular, scenarios using thread pools (threads in thread pools are often reused)
Memory leaks
Generally, ThreadLocal will be set as a static field when used. In this case, V in KV will not be reclaimed automatically after thread execution is completed. Therefore, remove() method should be called to clean up after using