How to use Java ThreadLocal class
As shown in the picture:
Next, 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:
Let’s first take a look at the class diagram structure of the ThreadLocal related class, as shown in the figure:
public 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);
}
}
Copy after login void createMap(Thread t, T firstValue) {
/**
* 这里是创建一个ThreadLocalMap,以当前调用线程的实例对象为key,初始值为value
* 然后放入当前线程的Therad.threadLocals属性里面
*/
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
Copy after login ThreadLocalMap getMap(Thread t) {
//这里就是直接获取调用线程的成员属性threadlocals
return t.threadLocals;
}
Copy after login
getpublic 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();
}
Copy after login
The following is the code for 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:php;toolbar:false;'>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:php;toolbar:false;'>/**
* 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); } }
Summary
Inside each thread there is a member variable named threadLocals, the type of this variable is Hash Map, where key is the this reference of the ThreadLocal variable we defined, and value is the value we set using the set method. The local variables of each thread are stored in the thread's own memory variable threadLocals. If the current thread never dies, these local variables will always exist, so it may cause memory overflow. Therefore, remember to call the remove method of ThreadLocal to delete after use. Local variables in threadLocals corresponding to the thread.
ThreadLocal memory leak
Why does a memory leak occur?
ThreadLocalMap uses the weak reference of ThreadLocal as the key. If a ThreadLocal does not have an external strong reference to refer to it, then the ThreadLocal will inevitably be recycled during the system GC.
In this way, the ThreadLocalMap will If an Entry with a null key appears, 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:
- Using static ThreadLocal extends the life cycle of ThreadLocal, which may lead to memory leaks
- Allocation uses ThreadLocal and no longer calls the get(), set(), remove() methods, which will lead to memory leaks
- Why use weak references?
Since we all know that using weak references will cause ThreadLocalMap memory leaks, why do officials still use weak references instead of strong references? This starts with the difference between using weak references and strong references:
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.
Solution
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!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics



Guide to Perfect Number in Java. Here we discuss the Definition, How to check Perfect number in Java?, examples with code implementation.

Guide to Random Number Generator in Java. Here we discuss Functions in Java with examples and two different Generators with ther examples.

Guide to Weka in Java. Here we discuss the Introduction, how to use weka java, the type of platform, and advantages with examples.

Guide to Smith Number in Java. Here we discuss the Definition, How to check smith number in Java? example with code implementation.

In this article, we have kept the most asked Java Spring Interview Questions with their detailed answers. So that you can crack the interview.

Java 8 introduces the Stream API, providing a powerful and expressive way to process data collections. However, a common question when using Stream is: How to break or return from a forEach operation? Traditional loops allow for early interruption or return, but Stream's forEach method does not directly support this method. This article will explain the reasons and explore alternative methods for implementing premature termination in Stream processing systems. Further reading: Java Stream API improvements Understand Stream forEach The forEach method is a terminal operation that performs one operation on each element in the Stream. Its design intention is

Guide to TimeStamp to Date in Java. Here we also discuss the introduction and how to convert timestamp to date in java along with examples.

Java is a popular programming language that can be learned by both beginners and experienced developers. This tutorial starts with basic concepts and progresses through advanced topics. After installing the Java Development Kit, you can practice programming by creating a simple "Hello, World!" program. After you understand the code, use the command prompt to compile and run the program, and "Hello, World!" will be output on the console. Learning Java starts your programming journey, and as your mastery deepens, you can create more complex applications.
