1. Background
Someone asked me recently ThreadLocal
how to isolate the value in each thread, I wrote an article here to simply record it.
Second, the problem solved by ThreadLocal
- This data belongs to the thread
Thread
itself, and other threads cannot affect it. (Note: you need to call the remove method of ThreadLocal) - There are no thread safety issues. (This is true because a variable of type
ThreadLocal
can only be accessed by its own thread.)
for example:
After the user logs in successfully, you need to save 登录用户信息
so that it can be used anywhere in the system, then you can use ThreadLocal
to achieve this. For example: Spring Security
ThreadLocalSecurityContextHolderStrategy
.
3. How to create a ThreadLocal instance
private static final ThreadLocal<String> USER_NAME = new ThreadLocal<>();
Instances of ThreadLocal are recommended to be decorated with private static final
.
Fourth, how ThreadLocal achieves thread variable isolation
1. Understand 3 categories
-
ThreadLocal
: 此类提供了一个简单的set
,get
,remove
方法,用于设置,获取或移除绑定to a value in a thread-local variable. ThreadLocalMap
: This is a class defined in ThreadLocal, which can be simply understood as a Map, but its key is WeakReference weak reference type, so when this value is not referenced elsewhere, When garbage collection occurs, the map'skey
will be automatically collected, but its value will not be automatically collected.static class Entry extends WeakReference<ThreadLocal<?>> { Object value; Entry(ThreadLocal<?> k, Object v) { // key 弱引用 super(k); // 值强引用 value = v; } }
-
Thread
: This is a thread class, there is athreadLocals
variable in this class, and the specific type isThreadLocal.ThreadLocalMap
.
2. See how the set method is implemented
public void set(T value) {
// 获取当前线程
Thread t = Thread.currentThread();
// 获取绑定到这个线程自身的 ThreadLocalMap,这个ThreadLocalMap是从Thread类的`threadLocals`变量中获取的
ThreadLocalMap map = getMap(t);
if (map != null) {
// 向map中设置值,key为 ThreadLocal 对象的实例。
map.set(this, value);
} else {
// 如果map不存在,则创建出来。
createMap(t, value);
}
}
Through the above code, we know: When we set a value to ThreadLocal, it will go through the following steps:
- Get current thread
Thread
- Get the
ThreadLocalMap
object of the current thread. - Set the value to
ThreadLocalMap
, the key is theThreadLocal
object, and the value is the specific value.
3. See how the get method is implemented
public T get() {
// 获取当前线程
Thread t = Thread.currentThread();
// 获取这个线程自身绑定的 ThreadLocalMap 对象
ThreadLocalMap map = getMap(t);
if (map != null) {
// this是ThreadLocal对象,获取Map中的Entry对象
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
// 获取具体的值
T result = (T)e.value;
return result;
}
}
// 设置初始值
return setInitialValue();
}
From the get and set methods above, we can know that by setting or getting the value in the ThreadLocal object, it is actually the final operation to the threadLocals field in the Thread object, and this field belongs to the Thread itself, so isolation is achieved.
Five, how to deal with the hash conflict in ThreadLocalMap
1. What is the hash value of the ThreadLocal object?
private final int threadLocalHashCode = nextHashCode();
// 该 ThreadLocal 对象自身的hash code值
private final int threadLocalHashCode = nextHashCode();
// 从0开始
private static AtomicInteger nextHashCode = new AtomicInteger();
// 每次递增固定的值
private static final int HASH_INCREMENT = 0x61c88647;
// hash code 值计算
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
从上方的代码中可以, ThreadLocal
类在实例化出来之后, hash code值(threadLocalHashCode)
就是固定的, ThreadLocal
set
method, set other values, its hash code值
will not change.
This field threadLocalHashCode
is the hash value of the ThreadLocal
object, which needs to be used in ThreadLocalMap
.
2. Resolve hash conflicts
ThreadLocalMap
The solution to the hash conflict is very simple. It is through the linear detection method. If there is a conflict, look for an available location at the back of the array. See the picture above for details. Demonstration is A and B 2 ThreadLocal objects, and then a conflict occurs, where A and B exist.
6. ThreadLocal memory leak
Why does ThreadLocal have memory leaks?
This is because the ThreadLocalMap
in key
is the WeakReference
type, which is a weak reference type, and the data of the weak reference type has no external strong reference type. When gc
occurs, it will be automatically recycled. 注意:
at this time key
was recovered, but value
was not recovered. ThreadLocalMap
中的Entry[]
中key
是null
,但是---4df09f2cfef818a00acf58c70a001b6e value
值的object, so a memory leak occurs.
Solve the memory leak:
When we finish using the ThreadLocal
object, we need to call the ThreadLocal#remove()
method at the appropriate time. Otherwise, it can only be cleared by waiting for Thread
to automatically exit. If the thread pool is used, Thread
will be reused, and the chance of clearing is more difficult.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。