As shown in the picture:
##Quick StartNext, we will use a simple example to show you the basic usage of ThreadLocal
package cuit.pymjl.thradlocal; /** * @author Pymjl * @version 1.0 * @date 2022/7/1 10:56 **/ public class MainTest { static ThreadLocal<String> threadLocal = new ThreadLocal<>(); static void print(String str) { //打印当前线程中本地内存中本地变量的值 System.out.println(str + " :" + threadLocal.get()); //清除本地内存中的本地变量 threadLocal.remove(); } public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { //设置线程1中本地变量的值 threadLocal.set("thread1 local variable"); //调用打印方法 print("thread1"); //打印本地变量 System.out.println("after remove : " + threadLocal.get()); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { //设置线程1中本地变量的值 threadLocal.set("thread2 local variable"); //调用打印方法 print("thread2"); //打印本地变量 System.out.println("after remove : " + threadLocal.get()); } }); t1.start(); t2.start(); } }
The running results are as shown in the figure:
The principle of ThreadLocalThreadLocal related class diagramLet’s first take a look at the class diagram structure of the ThreadLocal related class, as shown in the figure:
As can be seen from this figure, there is a threadLocals and an inheritableThreadLocals in the Thread class. They are both variables of the ThreadLocalMap type, and ThreadLocalMap is a customized Hashmap. By default, these two variables in each thread are null, and they are created only when the current thread calls the set or get method of ThreadLocal for the first time. In fact, the local variables of each thread are not stored in the ThreadLocal instance, but in the threadLocals variable of the calling thread. In other words, local variables of type ThreadLocal are stored in specific thread memory spaces. ThreadLocal is a tool shell that puts the value into the threadLocals of the calling thread through the set method and stores it. When the calling thread calls its get method, it takes it out from the threadLocals variable of the current thread for use. If the calling thread never terminates, then this local variable will always be stored in the threadLocals variable of the calling thread. Therefore, when the local variable is not needed, you can delete the local variable from the threadLocals of the current thread by calling the remove method of the ThreadLocal variable. In addition, why are the threadLocals in Thread designed as a map structure? Obviously because each thread can be associated with multiple ThreadLocal variables. Next, let's take a look at the source code of ThreadLocal's set, get, and removesetpublic void set(T value) { // 1.获取当前线程(调用者线程) Thread t = Thread.currentThread(); // 2.以当前线程作为key值,去查找对应的线程变量,找到对应的map ThreadLocalMap map = getMap(t); if (map != null) { // 3.如果map不为null,则直接添加元素 map.set(this, value); } else { // 4.否则就先创建map,再添加元素 createMap(t, value); } }
void createMap(Thread t, T firstValue) { /** * 这里是创建一个ThreadLocalMap,以当前调用线程的实例对象为key,初始值为value * 然后放入当前线程的Therad.threadLocals属性里面 */ t.threadLocals = new ThreadLocalMap(this, firstValue); }
ThreadLocalMap getMap(Thread t) { //这里就是直接获取调用线程的成员属性threadlocals return t.threadLocals; }
public T get() { // 1.获取当前线程 Thread t = Thread.currentThread(); // 2.获取当前线程的threadlocals,即ThreadLocalMap ThreadLocalMap map = getMap(t); // 3.如果map不为null,则直接返回对应的值 if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } // 4.否则,则进行初始化 return setInitialValue(); }
setInitialValue ##<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:java;">private T setInitialValue() {
//初始化属性,其实就是null
T value = initialValue();
//获取当前线程
Thread t = Thread.currentThread();
//通过当前线程获取ThreadLocalMap
ThreadLocalMap map = getMap(t);
//如果map不为null,则直接添加元素
if (map != null) {
map.set(this, value);
} else {
//否则就创建,然后将创建好的map放入当前线程的属性threadlocals
createMap(t, value);
}
//将当前ThreadLocal实例注册进TerminatingThreadLocal类里面
if (this instanceof TerminatingThreadLocal) {
TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
}
return value;
}</pre><div class="contentsignin">Copy after login</div></div>
I need to add some explanation here
. This class is new in jdk11 and does not exist in jdk8, so there is no relevant description of this class in many source code analyzes on the Internet. I took a look at the source code of this class, and its function should be to avoid the problem of ThreadLocal memory leaks (if you are interested, you can take a look at the source code, and please correct me if there are any errors). This is the official explanation: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:java;">/**
* A thread-local variable that is notified when a thread terminates and
* it has been initialized in the terminating thread (even if it was
* initialized with a null value).
* 一个线程局部变量,
* 当一个线程终止并且它已经在终止线程中被初始化时被通知(即使它被初始化为一个空值)。
*/</pre><div class="contentsignin">Copy after login</div></div>
remove
public void remove() { //如果当前线程的threadLocals 变量不为空, 则删除当前线程中指定ThreadLocal 实例的本地变量。 ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) { m.remove(this); } }
ThreadLocal memory leak
, there is no way to access the value of these Entry with a null key. If the current thread does not end for a long time, there will always be a strong line for the value of these Entry with a null key. Reference chain: Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value can never be recycled, causing memory leaks. In fact, this situation has been taken into consideration in the design of ThreadLocalMap, and some protective measures have been added: all keys in the thread ThreadLocalMap that are null will be cleared during get(), set(), and remove() of ThreadLocal. value. However, these passive preventive measures cannot guarantee that there will be no memory leaks:
If you use strong references: We know that the life cycle of ThreadLocalMap is basically the same as the life cycle of Thread. If the current thread is not terminated, then ThreadLocalMap will never be recycled by GC, and ThreadLocalMap holds the right to ThreadLocal. Strong reference, then ThreadLocal will not be recycled. When the thread life cycle is long, if it is not deleted manually, it will cause kv accumulation, resulting in OOM
If you use weak references: weak The object in the reference has a short declaration period, because during the system GC, as long as a weak reference is found, the object will be recycled regardless of whether the heap space is sufficient. When the strong reference of ThreadLocal is recycled, the weak reference held by ThreadLocalMap will also be recycled. If kv is not deleted manually, it will cause value accumulation and OOM
From the comparison, we can see that using weak references can at least ensure that OOM will not be caused by the accumulation of map keys, and the corresponding value can be cleared on the next call through the remove, get, and set methods. It can be seen that the root cause of memory leaks is not weak references, but the life cycle of ThreadLocalMap is as long as Thread, causing accumulation.
Since the root of the problem is the accumulation of value causing OOM, Then we take the right medicine and call the remove()
method every time we use ThreadLocal to clean it up.
The above is the detailed content of How to use Java ThreadLocal class. For more information, please follow other related articles on the PHP Chinese website!