当前位置:网站首页>Simple source code analysis of JUC - ThreadLocal / inheritablethreadlocal

Simple source code analysis of JUC - ThreadLocal / inheritablethreadlocal

2021-01-23 21:55:29 Mr. Xiaoqiang

One 、 Preface

When multiple threads use shared variables , If you want to ensure thread safety , It's usually locked ,synchronized perhaps Lock. But both locks are heavyweight , If multiple threads want to manipulate their own variables , They don't influence each other , that ThreadLocal That comes in handy ,InheritableThreadLocal It's an extension of its functions , We will analyze its usage scenarios later .

Two 、ThreadLocal Realization principle

Let's look at its class structure first :
image.png
In the red box is our usual method , It's used internally ThreadLocalMap Realized , Although the name has Map suffix , But it didn't work Map Interface , Take a look at its structure :

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;
        }
    }
    private Entry[] table;
}

You can see ,ThreadLocalMap It's used inside Entry[] To save thread variables ,key yes ThreadLocal The instance itself , It's not the current thread Thread Oh ,value That's the variable to use .
First look set() Method :

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

① Get the current thread first t Corresponding ThreadLocalMap example ,getMap The code is as follows :

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

You can see ,Thread There's a... Inside ThreadLocalMap Type references
Thread.java

ThreadLocal.ThreadLocalMap threadLocals = null;

② If map Not empty , Save the value ,this Is the present ThreadLocal example ,value For the variables we're going to use
③ If map It's empty , be createMap, The code is as follows :

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

Let's see ThreadLocalMap What does the constructor do :

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    //  initialization Entry[]
    table = new Entry[INITIAL_CAPACITY];
    //  Calculation firstValue In the position of the array i
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    //  assignment 
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
    //  Set the threshold , Expansion will use 
    setThreshold(INITIAL_CAPACITY);
}

setThreshold The method is as follows :

/**
 * Set the resize threshold to maintain at worst a 2/3 load factor. */
private void setThreshold(int len) {
    threshold = len * 2 / 3;
}

The code is also simple , Just look at the notes , Here's the realization idea and HashMap almost .
Look again. get() Method :

public T get() {
    //  Get the current thread t
    Thread t = Thread.currentThread();
    //  Get the current thread t Corresponding ThreadLocalMap
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        //  Get current ThreadLocal The thread variable corresponding to the instance 
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    // map If it is empty, initialize 
    return setInitialValue();
}

setInitialValue() The code is as follows :

private T setInitialValue() {
    //  Get the set initialization value , The default is null, The user can call it to set the initial value 
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    // map If it is not empty, set the initialization value , Otherwise create map
    if (map != null)
        map.set(this, value);
    else createMap(t, value);
    return value;
}

The last to see remove() Method :

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    //  Delete the current ThreadLocal The variable corresponding to the instance 
    if (m != null)
        m.remove(this);
}

frequently-used set(),get(),remove() The logic of the method is very clear , Let's not go over it

3、 ... and 、InheritableThreadLocal The role of

We know ,ThreadLocal In use , Variables between threads do not affect each other , The child thread can't get the local variable of the parent thread , It's normal . But sometimes there are demand scenarios like this , The child thread needs to get the variable of the parent thread , For example, subthreads need to be stored in ThreadLocal User login information in variable , For example, some middleware needs to unify id The entire call link traced is recorded . that InheritableThreadLocal You can do this . Let's see how it's implemented .

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

You can see ,InheritableThreadLocal Class inherited ThreadLocal, Rewrite the getMap() and createMap(), There's a... in it t.inheritableThreadLocals, It's the key to implementing shared variables , It's replaced t.ThreadLocals, All the original right t.ThreadLocals All the values of are changed to t.inheritableThreadLocals. It and t.threadLocals Same type , It's also Thread A property of .

ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

But at present, it seems that we still can't see which piece of code has the sharing effect ? It's hidden deep , stay Thread In the constructor of , Come and see :

public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
 long stackSize) {
    init(g, target, name, stackSize, null, true);
}
private void init(ThreadGroup g, Runnable target, String name,
 long stackSize, AccessControlContext acc,
 boolean inheritThreadLocals) {
 ...
 // parent It means calling new Thread() The thread of , namely main Threads 
 Thread parent = currentThread();
 ...
 if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
 ...
}

For the sake of explanation , Omitted some code , Just look right inheritableThreadLocals It works .
Let's see ThreadLocal.createInheritedMap() Code :

static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
    return new ThreadLocalMap(parentMap);
}
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) {
                // childValue() It's the parent thread e.value
                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++;
            }
         }
     }
}

You can see , The constructor is the function of the parent thread ThreadLocalMap Inside Entry[] Copy to child thread , Realize the sharing of variables .
That's the end of this article , Thank you for watching .

版权声明
本文为[Mr. Xiaoqiang]所创,转载请带上原文链接,感谢
https://chowdera.com/2021/01/20210123215424330x.html

随机推荐