Interview Literacy Series: ThreadLocal principle

How to use it?

ThreadLocal<String> localName = new ThreadLocal();
localName.set("xx");
String name = localName.get();

principle

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


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

First, look at the set method. In fact, value has ThreadLocalMap, and the Thread class has its own ThreadLocalMap member

[that is, a thread has a map specifically used to store the data of threadlocal objects as key s, and the open address method is used to solve hash conflicts],

for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) //Open address

It can be seen that ThreadLocal is a weak reference. When an object is found to have only weak references but no strong references during GC thread scanning, GC will reclaim its memory regardless of whether the current memory space is sufficient or not. [to prevent memory overflow, soft references and weak references can be used as much as possible when dealing with objects that occupy a large amount of memory and have a long life cycle.]

[understand weak citation examples: Java weak reference WeakReference understanding, this article is enough_ Cheng Feng's blog - CSDN blog_ Inherit weak references]

static class ThreadLocalMap {
    static class Entry extends WeakReference<ThreadLocal<?>> {
			Object value;
			Entry(ThreadLocal<?> k, Object v) {
				super(k);
				value = v;
			}
		}
}

Memory leak?

        // Strong reference to ThreadLocal object
        ThreadLocal<String> threadLocal = new ThreadLocal<>();
        threadLocal.set("abc");

        // No strong reference to ThreadLocal object
        new ThreadLocal<>().set("def");

When no strong reference exists, the object pointed to by the weak reference will be recycled by the garbage collector. However, although the key is recycled here, the value will still leak memory. Value can only be recycled by gc when the thread life cycle ends or the cleanup algorithm is triggered.

If our ThreadLocal object is also a static constant at this time, the next time the thread is used, it is likely to get the previously saved data, resulting in dirty data.

How to avoid memory leakage?

remove in time

		private void remove(ThreadLocal<?> key) {
			Entry[] tab = table;
			int len = tab.length;
			int i = key.threadLocalHashCode & (len - 1);
			for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
				if (e.get() == key) {
					e.clear();
					expungeStaleEntry(i);
					return;
				}
			}
		}

Magical 0x61c88647

Make the hash distribution very uniform. [ ThreadLocal interview, this one is enough_ Xiaodai's column - CSDN blog]

int h = key.threadLocalHashCode & (len - 1);

If there is a new number, calculate its hash value, key Threadlocalhashcode adds 0x61c88647 to the original

InheritableThreadLocal 

When using ThreadLocal, the child thread cannot obtain the data saved by the parent thread through the set method. To enable the child thread to obtain it, you can use the InheritableThreadLocal class.

public class InheritableThreadLocal<T> extends ThreadLocal<T> {

    protected T childValue(T parentValue) {
        return parentValue;
    }

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

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

The principle is super simple. When calling set, it is actually going to ThreadLocal of Thread class ThreadLocalMap inheritableThreadLocals = null; It's written here. Why can a sub Thread get it?

public class Thread implements Runnable {
  private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) {

    if (parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
}}

The data in the child thread is copied from the parent thread, so the content re set in the child thread is invisible to the parent thread.

Other reference materials

Java interview must ask, ThreadLocal ultimate article - simple book

Interview question: tragedy caused by threadlocal_ zuihongyan518 blog - CSDN blog_ threadlocal interview

Keywords: Java ThreadLocal

Added by lauxanh on Sat, 29 Jan 2022 00:52:18 +0200