ThreadLocal of Java concurrency

ThreadLocal allows shared variables. In a multi-threaded environment, each thread maintains a copy of the shared variables and operates thread local variables independently of each other to avoid thread safety problems.  

Looper in Android uses ThreadLocal to save looper objects of multiple threads.

The relationship among Thread, ThreadLocal and ThreadLocalMap is as follows:

1, ThreadLocal definition

public class ThreadLocal<T> {
    public ThreadLocal() {
    }
    ...

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
    ...

    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;
    }

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

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

The usage of ThreadLocal is very simple. The new ThreadLocal(), set(), get() and remove() methods can be used normally. The internal operations of set(), get() and remove() are the ThreadLocalMap method.

ThreadLocalMap m = getMap(Thread.currentThread());

The {threadLocals field of the thread when ThreadLocal. Is called The set () method is initialized.

2, ThreadLocalMap

ThreadLocalMap is a custom hash map used to maintain thread local variable values.

The Entry class is implemented as follows:

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

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

The entry [] attribute is used to store the value corresponding to {ThreadLocal. The entry [] subscript is determined by the hashCode of threadLoadl, as follows:

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

3, InheritableThreadLocal

ThreadLocal is a copy of shared variables held by each thread, and the child thread cannot access the value of the parent thread. InheritableThreadLocal solves this problem. The child thread can access the local variables of the parent thread. The source code is as follows:

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);
    }
}

It inherits ThreadLocal and has only three methods: childValue(), getMap() and createMap(). You can see that the map is replaced by the ThreadLocal of the parent thread, which is different from ThreadLocal.

3.1} how does a child thread inherit the value of the parent thread

public class Thread implements Runnable {
    ...
   public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }
    ... 
    
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
        ...    
        init2(parent);
        ... 
    }

    private void init2(Thread parent) {
        this.contextClassLoader = parent.getContextClassLoader();
        this.inheritedAccessControlContext = AccessController.getContext();
        if (parent.inheritableThreadLocals != null) {
            this.inheritableThreadLocals = ThreadLocal.createInheritedMap(
                    parent.inheritableThreadLocals);
        }
    }
    ... 
}
public class ThreadLocal<T> {
    ...    
    static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }
    ...

    static class ThreadLocalMap {
        ...

        private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

        ...
    }
}

The above is the process created by the thread. From init2(), you can see that only when parent inheritableThreadLocals != Null, the child thread will inherit the value of the parent thread. The inheritablethreadlocals property of the parent thread is assigned in createMap(). createMap() will be executed only when set() is called to obtain that threadLocalMap is empty.

Key point: if the child thread wants to inherit the value of the parent thread, the parent thread must call ThreadLocal before creating the child thread Set () method so that it can inherit.

Note:

        1. Immutable objects (wrapper classes and strings of basic types) are new objects for each operation, with different indexes, so they are independent and do not affect each other.

        2. Variable object (reference type): the parent thread is not initialized and the child thread will not inherit when it is created, so the parent thread and child thread reference different objects separately.

        3. Variable object (reference type): the parent thread has initialization, and the child thread will inherit when it is created. Therefore, the parent and child threads share the same object index. If the child thread modifies the attribute, the parent thread will also change (premise: the child thread must obtain the object instance through get() to modify the same object instance; If you directly set() a new object, the parent and child threads will hold different indexes, thus opposing each other).

4, Other

1. Improper use of ThreadLocal may lead to memory leakage

Entry in ThreadLocalMap uses ThreadLocal weak reference as key. During the running of the current thread, if the {ThreadLocal variable is not strongly referenced, it will be recycled during} GC, which leads to an entry data with null key and non null value in ThreadLocalMap, resulting in memory leakage.

Solution: use ThreadLocal to manually call the remove method.

Keywords: Java Multithreading

Added by newbiez on Sat, 25 Dec 2021 18:50:59 +0200