ThreadLocal in Java

    The sharing of variable values ​​can be in the form of public static variables, and all threads can share a variable. If we want to realize that a thread has its own shared variable how to solve it, we need to use the ThreadLocal class in Java.

    The main solution of the class ThreadLocal is that each thread binds its own value. The ThreadLocal class can be compared to a box that stores data globally. The box can store the private data of each thread.

    Let's look at the following code

public static void main(String[] args) throws InterruptedException {
    ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
        @Override
protected Integer initialValue() {
            return 0;
}                
    };
threadLocal.set(100);
System.out.println("主线程中的值:"+threadLocal.get());
Thread t = new Thread(){
        @Override
public void run() {
            threadLocal.set(200);
System.out.println("线程t中的值:"+threadLocal.get());
}
                                        
    };
t.start();
}
    

The code defines a ThreadLocal object, and then in the main thread, puts 100 in the ThreadLocal object, and 200 in thread t, and prints it. The result is as follows


It can be seen that each thread has obtained the value stored by itself. So how is it achieved, let's analyze it

First, let's introduce some main methods of ThreadLocal

1. initalValue method

protected T initialValue() {
    return null;
}

The initalValue method, which represents the default value, can be seen as null, but for the sake of safety, we use an anonymous inner class to rewrite this method when initializing the ThreadLocal object to prevent null pointer exceptions. That's it:

ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
    @Override
protected Integer initialValue() {
        return 0;
}        
};

2. get method

public T get() {
     // Get the current thread object
     Thread t = Thread.currentThread () ;
 // Get the ThreadLocalMap in the thread class ThreadLocal.ThreadLocalMap
 map = getMap(t) ;
     if (map != null ) {        
        ThreadLocal.ThreadLocalMap.Entry e = map.getEntry( this ) ;
 // If the Map is not empty and the thread does store the variable, return the stored variable
 if (e != null ) {
             @SuppressWarnings ( "unchecked" )                
            T result = (T)e.value;
            return result;
        }
    }
    //Map is empty, return default value
 return setInitialValue() ;
 }    

It should be mentioned here that a ThreadLocalMap object is maintained in each thread, and this Map stores the values ​​you store, which will be discussed below.

3, set method

public void set(T value) {
     // Get the current thread object
     Thread t = Thread.currentThread ( ) ;
 // Get the ThreadLocalMap in the thread according to the thread ThreadLocal.ThreadLocalMap
 map = getMap (t) ;
 // If the map is not empty, then Store the value directly
 if (map != null )                
        map.set( this, value) ;
 // If the map is empty, create a Map and store the value else
 createMap(t , value) ;
 }    
            

4. remove method

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
}

remove是移除所存入的值

5、getMap方法

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

根据线程去获得线程中的ThreadLocalMap对象

我们可以打开Thread类源码

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

这就是Thread类中的ThreadLocalMap对象,默认为空。只有你在使用ThreadLocal为线程保存私有属性时才会创建。

6、createMap方法

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

将线程中的ThreadLocalMap对象实例化并写入存入值

接下来就要看存储数据的ThreadLocalMap

static class ThreadLocalMap {
    //Map节点,注意keyThreadLocal而不是Thread
    static class Entry extends WeakReference<ThreadLocal<?>> {
        Object value;
        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
    //构造方法
    ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
        table = new ThreadLocal.ThreadLocalMap.Entry[INITIAL_CAPACITY];
        int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
        table[i] = new ThreadLocal.ThreadLocalMap.Entry(firstKey, firstValue);
        size = 1;
        setThreshold(INITIAL_CAPACITY);
    }
}

Map很简单,只不过要注意的是Map的键是ThreadLocal而不是Thread。

总结

    1、ThreadLocal是为了解决每个线程绑定自己的值

    2、ThreadLocal默认值为null,所以有时候为了安全,初始化时使用匿名内部类来重写initalValue方法

    3、每个线程对象内部都维护了一个ThreadLocalMao对象

    4、ThreadLocalMap的键是ThreadLocal而不是Thread

    5、ThreadlLocal可能会发生内存泄漏,所以在使用完ThreadLocal之后调用remove方法清除数据。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325667170&siteId=291194637