Detailed explanation of java thread local variable ThreadLocal

Time:2020-2-10

introduce

ThreadLocal, as a class under java.lang package since JDK 1.2, is very important in interviews and projects. The main purpose of this class is to provide thread local variables, so this class is also called thread local variables in many places

Literally, this class creates a local variable for each thread. In fact, ThreadLocal creates a copy for the variable in each thread, so that each thread can access its internal copy variable

Generally speaking, when it comes to multithreading, variable synchronization is considered. However, ThreadLocal is not to solve the problem of multithreading sharing variable synchronization, but to ensure that variables of each thread do not affect each other. It is equivalent to that the copies of variables are manipulated between threads. Naturally, the problem of multithreading competition is not considered, and there is no performance loss

Usage mode

Let’s take a look at the common methods

public T get() { }
public void set(T value) { }
public void remove() { }
protected T initialValue() { }

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 first take a look at the following example and experience 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() {
return 10;
}
};
MyThread t1 = new MyThread(20);
MyThread t2 = new MyThread(30);
t1.start();
//In order to describe clearly, try catch block is omitted
t1.join();
t2.start();
}
}

In the above method, we define and initialize a ThreadLocal class as 10 (by overriding the initialvalue() method), and then start two threads. At the same time, we let T2 thread wait for T1 thread to finish executing before 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() {
System.out.println(Thread.currentThread() + "-BEFORE-" + Demo.threadLocal.get());
Demo.threadLocal.set(val);
System.out.println(Thread.currentThread() + "-AFTER-" + Demo.threadLocal.get());
}
}

We get the current value by calling the get() method of ThreadLocal object, then set a new value through the set() method (we set different values for each thread), and then get the set value through the get() method

The operation results are as follows

The key point is the initial value of T2 thread variable marked in the figure. Although we have modified the value of variable in T1 thread, the value of variable in T2 thread has not been changed, thus realizing the unique variable of each thread

At the same time, if a ThreadLocal object needs to be reused in many places, you need to call the * * remove() * * method to restore the local variable to the default value before using

It may be asked that we can define our own private variables for each thread to achieve the same operation, which is certainly feasible in theory, but ThreadLocal is far more convenient than the form of private variables. It can not only perform unified initialization outside the thread, but also avoid setting additional variables inside the thread

principle

Click the source code of ThreadLocal, and it is found that there is no field value to store variables. It seems that ThreadLocal is not responsible for saving variables. We can only start with methods

First look at the initial () method. After all, the default initial value of our variables is set in this method, as follows


protected T initialValue() {
return null;
}

We need to rewrite this method every time we create ThreadLocal, so where is this method called? Let’s click the get () method source code, as follows

public T get() {
//Get current thread
Thread t = Thread.currentThread();
//Get current thread的map
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
T result = (T)e.value;
return result;
}
}
//Create if map is empty
return setInitialValue();
}

A little frown, we find that we get a threadlocalmap object here, so we may think that each thread has its own unique variables by making a kV table of threads and variables

When we click into the getmap (T) method, we find that a threadlocations property of thread t is returned, which is a field of thread class:


ThreadLocal.ThreadLocalMap threadLocals = null;

This is a property maintained by the ThreadLocal class. No method of thread has modified this field. The threadlocalmap itself is an internal class of ThreadLocal. It can be understood as a map (although this class does not inherit the map interface)

At the same time, note that the entry object (key value pair) in the threadlocalmap object inherits a weak reference of 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;
}
}

That is, when ThreadLocal is set to null, the key in the entry will be recycled in the next YGC

We still don’t see the initialvalue() method. Don’t worry, click the setinitialvalue() method, that is, if we detect that the map is empty in the get() method, we will call it as follows

private T setInitialValue() {
//The initial value we set
T value = initialValue();
//Current thread
Thread t = Thread.currentThread();
//Check again if it is empty
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}

Now we are clear about the basic operations. The set () method is almost identical with the initialvalue (). The remove () method removes a kV key value pair (k is the current thread), which is not listed here. If you are interested, you can view it yourself

Matters needing attention

1. dirty data

From the above analysis, we can see that ThreadLocal is bound to Thread, and each Thread corresponds to a value. If we do not call remove () after the end of the application, we will read dirty data in the next reuse (for the same thread), especially the thread pool (the thread in the thread pool often reuses).

2. Memory leak

In general, ThreadLocal will be set as a static field when using. At this time, when the thread execution is completed, V in kV will not be recycled automatically, so you need to call remove() method to clean up in time after using

The above is the whole content of this article. I hope it will help you in your study, and I hope you can support developepaer more.

Recommended Today

Webflux series (VI) webclient vs resttemplate

Java#Spring#WebFlux#WebClient#RestTemplate Performance comparison between webclient and resttemplate Video Explanation: https://www.bilibili.com/videoServer sideWebfluxServerApplication.java package com.example.webfluxserver; import lombok.extern.log4j.Log4j2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @Log4j2 @SpringBootApplication public class WebfluxServerApplication extends BaseApplication { public static void main(String[] args) { SpringApplication.run(WebfluxServerApplication.class, args); } @RestController class EmployeeController { @GetMapping(“employees”) public List<Employee> findAll() throws InterruptedException { Thread.sleep(5000); return […]