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

  1. This data belongs to the thread Thread itself, and other threads cannot affect it. (Note: you need to call the remove method of ThreadLocal)
  2. 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

  1. ThreadLocal : 此类提供了一个简单的set , get , remove方法,用于设置,获取或移除绑定to a value in a thread-local variable.
  2. 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's key 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;
        }
    }
  3. Thread : This is a thread class, there is a threadLocals variable in this class, and the specific type is ThreadLocal.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:

  1. Get current thread Thread
  2. Get the ThreadLocalMap object of the current thread.
  3. Set the value to ThreadLocalMap , the key is the ThreadLocal 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[]keynull ,但是---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.


huan1993
218 声望34 粉丝

java工程师