[JAVA core knowledge] 18: thread local variable ThreadLocal

 

ThreadLocal is jdk1 2, which is used to provide local variables in the thread. Unlike ordinary variables, thread local variables are bound to threads, and each thread has its own independent variable container. Thread local variables play a role in the life cycle of a thread to reduce the transmission complexity of common variables between multiple functions or components in a thread.

ThreadLocal

Use example

Implementation analysis

Use example

public class ThreadLocalDemo {

    static final ThreadLocal<String> TL_DEMO = new ThreadLocal<>();
    public static void main(String[] args) {
        Runnable rn1 = new Runnable() {
            @Override
            public void run() {
                setVal("R1 TEST STRING");
                printVal();
            }
        };
        Runnable rn2 = new Runnable() {
            @Override
            public void run() {
                setVal("R2 TEST STRING");
                printVal();
            }
        };
        new Thread(rn1).start();
        new Thread(rn2).start();
    }
    
    public static void setVal(String val) {
        TL_DEMO.set(val);
    }

    public static void printVal() {
        System.out.println(TL_DEMO.get());
    }
}

Operation results:

R1 TEST STRING
R2 TEST STRING

Seeing this, you may say that I can achieve this with Map. Why use ThreadLocal. First, ThreadLocal is bound to the thread. Each thread is independent of each other and does not affect each other. You can obtain the thread local variables bound to the current thread through ThreadLocal anywhere. Of course, this is also achieved through the Map with the thread ID as the Key. Secondly, the most important thing is that the thread local variables stored in ThreadLocal will automatically enter the recyclable state with the destruction of the thread. When using Map, you need to clear the Map displayed before the thread dies, otherwise the variables have strong connections and cannot enter the recyclable state.

Although we can realize the functions of ThreadLocal through other ways, they are not as simple as ThreadLocal. The significance of the existence of tool classes is here. Integrate complex function implementations and provide simple use methods to make programming simpler and code clearer.

Implementation analysis

public void set(T value) {
	Thread t = Thread.currentThread();
	ThreadLocalMap map = getMap(t);
	if (map != null)
		map.set(this, value);
	else
		createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
	return t.threadLocals;
}

Through the set method above, we can see that the actual implementation of Thread is realized through Map. The attribute threadlocales is maintained inside Thread:

ThreadLocal.ThreadLocalMap threadLocals = null;

The container of ThreadLocalMap is Thread$ThreadLocalMap$Entry [], and the reference relationship is shown in the figure:

For Thread$ThreadLocalMap$Entry:

private Entry[] table;
static class Entry extends WeakReference<ThreadLocal<?>> {
	/** The value associated with this ThreadLocal. */
	Object value;

	Entry(ThreadLocal<?> k, Object v) {
		super(k);
		value = v;
	}
}

You can see that the Entry inherits the WeakReference, which indicates that the Entry's reference to ThreadLocal is Weak reference , which means that when the strong ThreadLocal reference we hold disappears, the ThreadLocal instance can enter the recyclable state. However, the Entry instance will not enter the recycling state.

For ordinary threads, because ThreadLocalMap is maintained inside the Thread, when the Thread dies, the Thread local variable container ThreadLocalMap maintained by it will also enter the recyclable state. However, for Thread pool threads, we know that during the operation of Thread pool Core thread of thread pool If it is not set, it will not be destroyed, which means that the ThreadLocalMap maintained by the thread will always exist during the running of the thread pool. At this time, you can manually call ThreadLocal Remove () causes the strong reference of the Entry instance to disappear and enter the recyclable state.

However, there is no place to say that using ThreadLocal requires mandatory call to ThreadLocal For the remove () method, if there is no recycling and new objects come in all the time, why won't it cause OOM? This is because ThreadLocal will have an automatic lazy cleaning method. ThreadLocalMap is stored differently from HashMap Adopted Zipper method , but the Open address method:

/**Subscript confirmation*/
for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)])
/**Subscript confirmation*/

private static int nextIndex(int i, int len) {
	return ((i + 1 < len) ? i + 1 : 0);
}

When a Hash conflict is encountered and an invalid Entry instance whose Key has been recycled is found, some container slots will be scanned and cleared tentatively (not limited to the conflict, but a certain range of scanning) to make the Entry enter a recyclable state.

So why not set value to weak reference? In this way, it is not necessary to manually execute ThreadLocal Remove() to achieve the same automatic recycling as ThreadLocal instance? This is because ThreadLocal cannot determine when the program logic will disconnect the strong reference holding of the value instance. If value is also set to weak reference, then after the program logic loses the strong reference holding, value will be recycled at the next GC, but maybe the program still holds the ThreadLocal instance at this time, In the next run, get the thread local variable value through the ThreadLocal instance, and then the value has been GC at this time, causing an unexpected error.

Nevertheless, using ThreadLocal in thread pool mode is still recommended to call ThreadLocal where appropriate Remove() recycles objects in time to avoid memory leakage.

PS:
[JAVA core knowledge] series navigation [constantly updating...]
Previous navigation: 17.2: inter thread communication cooperation - exchange
Next Trailer: 19: synchronized and ReentrantLock
Welcome to

Keywords: Java Multithreading

Added by shu on Sun, 30 Jan 2022 14:05:07 +0200