[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/HiAscend/Java-Guide/master/docs/java/Multithread/ThreadLocal.md [Back]  [Original]

>   [https://juejin.im/post/5eacc1c75188256d976df748](https://juejin.im/post/5eacc1c75188256d976df748)

### 

![](./thread-local/1.png)

**10000+31+**

`ThreadLocal`

- `ThreadLocal`key**** `ThreadLocal`.get(),**GC**key**null**
- `ThreadLocal``ThreadLocalMap`****
- `ThreadLocalMap`**Hash**
- `ThreadLocalMap`**Hash**
- `ThreadLocalMap`****
- `ThreadLocalMap`**key**********
- `ThreadLocalMap.set()`
- `ThreadLocalMap.get()`
- `ThreadLocal`
- ......

`ThreadLocal`****

### 


**** `JDK 1.8`

### `ThreadLocal`

`ThreadLocal`

```java
public class ThreadLocalTest {
    private List messages = Lists.newArrayList();

    public static final `ThreadLocal` holder = `ThreadLocal`.withInitial(ThreadLocalTest::new);

    public static void add(String message) {
        holder.get().messages.add(message);
    }

    public static List clear() {
        List messages = holder.get().messages;
        holder.remove();

        System.out.println("size: " + holder.get().messages.size());
        return messages;
    }

    public static void main(String[] args) {
        ThreadLocalTest.add("");
        System.out.println(holder.get().messages);
        ThreadLocalTest.clear();
    }
}
```



```java
[]
size: 0
```

`ThreadLocal``Thread`****

### `ThreadLocal`

![](./thread-local/2.png)


`Thread```ThreadLocal`.`ThreadLocalMap```threadLocals``ThreadLocalMap`

`ThreadLocalMap``key``ThreadLocal``value``key``ThreadLocal`****

`ThreadLocal``ThreadLocalMap``ThreadLocal``map``key`****

`ThreadLocalMap``HashMap``HashMap`**+**`ThreadLocalMap`****

`Entry` `key```ThreadLocal` k` `WeakReference 

###  GC keynull

 `ThreadLocal` `key`` `ThreadLocal`.get()`,`GC``key``null`

`Java`****

- ****new
- ****SoftReference
- ****WeakReference
- **** Java  PhantomReference 


`GC``ThreadLocal`(https://blog.csdn.net/thewindkee/article/details/103726942GC)

```java
public class ThreadLocalDemo {

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InterruptedException {
        Thread t = new Thread(()->test("abc",false));
        t.start();
        t.join();
        System.out.println("--gc--");
        Thread t2 = new Thread(() -> test("def", true));
        t2.start();
        t2.join();
    }

    private static void test(String s,boolean isGC)  {
        try {
            new `ThreadLocal`().set(s);
            if (isGC) {
                System.gc();
            }
            Thread t = Thread.currentThread();
            Class tlmClass = `ThreadLocalMap`.getClass();
            Field tableField = tlmClass.getDeclaredField("table");
            tableField.setAccessible(true);
            Object[] arr = (Object[]) tableField.get(`ThreadLocalMap`);
            for (Object o : arr) {
                if (o != null) {
                    Class entryClass = o.getClass();
                    Field valueField = entryClass.getDeclaredField("value");
                    Field referenceField = entryClass.getSuperclass().getSuperclass().getDeclaredField("referent");
                    valueField.setAccessible(true);
                    referenceField.setAccessible(true);
                    System.out.println(String.format("key:%s,:%s", referenceField.get(o), valueField.get(o)));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
```


```java
key:java.lang.`ThreadLocal`@433619b6,:abc
key:java.lang.`ThreadLocal`@418a15e3,:java.lang.ref.SoftReference@bf97a12
--gc--
key:null,:def
```

![](./thread-local/3.png)

`ThreadLocal`

```java
new ThreadLocal().set(s);
```

`GC``key``debug``referent=null`, ****

![](./thread-local/4.png)

********`null`

 ``ThreadLocal`.get()` **** `key`  `null``ThreadLocal`****

![image.png](./thread-local/5.png)

**** `key`  `value` `key`  `value` 

### `ThreadLocal.set()`

![](./thread-local/6.png)

`ThreadLocal``set``ThreadLocalMap``ThreadLocal``set`



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

`ThreadLocalMap`

### `ThreadLocalMap` Hash

`Map``ThreadLocalMap``hash`

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

`ThreadLocalMap``hash``i`key

`threadLocalHashCode``ThreadLocal``HASH_INCREMENT = 0x61c88647`

```java
public class ThreadLocal {
    private final int threadLocalHashCode = nextHashCode();

    private static AtomicInteger nextHashCode = new AtomicInteger();

    private static final int HASH_INCREMENT = 0x61c88647;

    private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }
    
    static class `ThreadLocalMap` {
        `ThreadLocalMap`(`ThreadLocal` firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);

            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
    }
}
```

`ThreadLocal```ThreadLocal`.nextHashCode`  `0x61c88647` 

****   ****`hash`  `hash` ****



![](./thread-local/8.png)

****

### `ThreadLocalMap` Hash

> **** ****`Entry`********`Entry``key``null`********`Entry``null`

`ThreadLocalMap`****`hash``Hash`

`HashMap`********

`ThreadLocalMap``HashMap`

![](./thread-local/7.png)


`value=27``hash`44`Entry`

`Entry``null``Entry``null``key``Entry``key``null`

`Entry``key``null`**Entry=2**`key`****`set``key``Entry`****

### `ThreadLocalMap.set()`

#### `ThreadLocalMap.set()`

`ThreadLocal` **hash**`set`

`ThreadLocalMap``set`********

**** `hash``Entry`

![](./thread-local/9.png)



**** `key``ThreadLocal``hash``key`

![](./thread-local/10.png)



**** `Entry``null``key``Entry`

![](./thread-local/11.png)

`Entry``null`**key**

**** `Entry``null``key``Entry``index=7``Entry``key=null`

![](./thread-local/12.png)

7`Entry``key``null``key``replaceStaleEntry()`******index=7**

`slotToExpunge = staleSlot = 7`

`staleSlot` `slotToExpunge``for``Entry``null`

`Entry=null`**slotToExpunge0**

![](./thread-local/13.png)

(`index=7`)`Entry``slotToExpunge``null``slotToExpunge`0

`slotToExpunge``staleSlot`

`staleSlot`(index=7)**keyEntry**

![](./thread-local/14.png)

`staleSlot``key``Entry``Entry``staleSlot`(`staleSlot`)`Entry``Entry`

![Yu4oWT.png](https://user-gold-cdn.xitu.io/2020/5/8/171f3ba9af057e1e?w=1336&h=361&f=png&s=63049)


**keyEntry**

![](./thread-local/15.png)

`staleSlot``key``Entry``Entry``null``table``key``Entry`

`Entry``table[stableSlot]`

![](./thread-local/16.png)

`expungeStaleEntry()``cleanSomeSlots()`

#### `ThreadLocalMap.set()`

`set()`

`java.lang.ThreadLocal`.`ThreadLocalMap.set()`:

```java
private void set(ThreadLocal key, Object value) {
    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)]) {
        `ThreadLocal` k = e.get();

        if (k == key) {
            e.value = value;
            return;
        }

        if (k == null) {
            replaceStaleEntry(key, value, i);
            return;
        }
    }

    tab[i] = new Entry(key, value);
    int sz = ++size;
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        rehash();
}
```

`key``key`

```java
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
```


1. `k = key` 
2. 
3. `Entry=null`

`for``nextIndex()``prevIndex()`

![](./thread-local/17.png)

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

private static int prevIndex(int i, int len) {
    return ((i - 1 >= 0) ? i - 1 : len - 1);
}
```

`for`
1. `key``Entry``for``set`
2. `key``Entry`
2.1 `k = key``set`
2.2 `key = null``Entry``replaceStaleEntry()`()
3. `for``entry``null`
3.1 `Entry``null``Entry`
3.2 `++size`
4. `cleanSomeSlots()``Entry``key`
4.1 `size`(2/3)`rehash()`
4.2 `rehash()``key`**size >= threshold - threshold / 4**()

`replaceStaleEntry()``replaceStaleEntry()`****

`java.lang.ThreadLocal.ThreadLocalMap.replaceStaleEntry()`:

```java
private void replaceStaleEntry(`ThreadLocal` key, Object value,
                                       int staleSlot) {
    Entry[] tab = table;
    int len = tab.length;
    Entry e;

    int slotToExpunge = staleSlot;
    for (int i = prevIndex(staleSlot, len);
         (e = tab[i]) != null;
         i = prevIndex(i, len))

        if (e.get() == null)
            slotToExpunge = i;

    for (int i = nextIndex(staleSlot, len);
         (e = tab[i]) != null;
         i = nextIndex(i, len)) {

        `ThreadLocal` k = e.get();

        if (k == key) {
            e.value = value;

            tab[i] = tab[staleSlot];
            tab[staleSlot] = e;

            if (slotToExpunge == staleSlot)
                slotToExpunge = i;
            cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
            return;
        }

        if (k == null && slotToExpunge == staleSlot)
            slotToExpunge = i;
    }

    tab[staleSlot].value = null;
    tab[staleSlot] = new Entry(key, value);

    if (slotToExpunge != staleSlot)
        cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
}
```

`slotToExpunge``staleSlot``staleSlot``for``Entry``null`i`slotToExpunge=i`

```java
for (int i = prevIndex(staleSlot, len);
     (e = tab[i]) != null;
     i = prevIndex(i, len)){

    if (e.get() == null){
        slotToExpunge = i;
    }
}
```

`staleSlot``Entry``null`
**k == key**`staleSlot``slotToExpunge == staleSlot``replaceStaleEntry()``Entry`index`slotToExpunge = i``cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);`

```java
if (k == key) {
    e.value = value;

    tab[i] = tab[staleSlot];
    tab[staleSlot] = e;
 
    if (slotToExpunge == staleSlot)
        slotToExpunge = i;

    cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
    return;
}
```

`cleanSomeSlots()``expungeStaleEntry()``key``Entry`(`Heuristically scan`)`key``Entry`

**k != key**`k == null``Entry``slotToExpunge == staleSlot``Entry``slotToExpunge` 

```java
if (k == null && slotToExpunge == staleSlot)
    slotToExpunge = i;
```

`k == key``Entry``null``table[staleSlot]` `slot`

```java
tab[staleSlot].value = null;
tab[staleSlot] = new Entry(key, value);
```

`staleSlot``slot`
```java
if (slotToExpunge != staleSlot)
    cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
```

### `ThreadLocalMap`key

`ThreadLocalMap``key`********

`expungeStaleEntry``Entry``null``rehash``table``Entry=null``rehash``Entry`

![](./thread-local/18.png)

`set(27)` hash`index=4``index=4``index=7``index=5``Entry``key``null`

![](./thread-local/19.png)

`set``map`****

****`index=5``index=7``rehash``index=4``index=4``Entry=null`(index=5)`index= 7``index=5``index=4`

`key``rehash``i= key.hashCode & (tab.len - 1)`

`expungeStaleEntry()`

![](./thread-local/20.png)

`expungeStaleEntry(3)` `ThreadLocalMap``table`

![](./thread-local/21.png)

`staleSlot``index=3``Entry``null`

![](./thread-local/22.png)

index=4index=3

`slot`

![](./thread-local/23.png)

****

```java
private int expungeStaleEntry(int staleSlot) {
    Entry[] tab = table;
    int len = tab.length;

    tab[staleSlot].value = null;
    tab[staleSlot] = null;
    size--;

    Entry e;
    int i;
    for (i = nextIndex(staleSlot, len);
         (e = tab[i]) != null;
         i = nextIndex(i, len)) {
        `ThreadLocal` k = e.get();
        if (k == null) {
            e.value = null;
            tab[i] = null;
            size--;
        } else {
            int h = k.threadLocalHashCode & (len - 1);
            if (h != i) {
                tab[i] = null;

                while (tab[h] != null)
                    h = nextIndex(h, len);
                tab[h] = e;
            }
        }
    }
    return i;
}
```

`staleSlot=3` `tab[staleSlot]``size--`
`staleSlot``k==null``size--`

```java
ThreadLocal k = e.get();

if (k == null) {
    e.value = null;
    tab[i] = null;
    size--;
} 
```

`key``key``hash``entry`

```java
int h = k.threadLocalHashCode & (len - 1);
if (h != i) {
    tab[i] = null;

    while (tab[h] != null)
        h = nextIndex(h, len);

    tab[h] = e;
}
```

`Hash``Hash``Entry` 

### `ThreadLocalMap`

``ThreadLocalMap.set()``Entry``(len*2/3)``rehash()`

```java
if (!cleanSomeSlots(i, sz) && sz >= threshold)
    rehash();
```

`rehash()`

```java
private void rehash() {
    expungeStaleEntries();

    if (size >= threshold - threshold / 4)
        resize();
}

private void expungeStaleEntries() {
    Entry[] tab = table;
    int len = tab.length;
    for (int j = 0; j < len; j++) {
        Entry e = tab[j];
        if (e != null && e.get() == null)
            expungeStaleEntry(j);
    }
}
```

`table``table``key``null``Entry``size >= threshold - threshold / 4` `size >= threshold* 3/4` 

`rehash()``size >= threshold``ThreadLocalMap` 

![](./thread-local/24.png)

`resize()``oldTab.len=8`

![](./thread-local/25.png)

`tab``oldLen * 2``hash``tab``hash``entry``null``oldTab``entry``tab``tab`****

```java
private void resize() {
    Entry[] oldTab = table;
    int oldLen = oldTab.length;
    int newLen = oldLen * 2;
    Entry[] newTab = new Entry[newLen];
    int count = 0;

    for (int j = 0; j < oldLen; ++j) {
        Entry e = oldTab[j];
        if (e != null) {
            `ThreadLocal` k = e.get();
            if (k == null) {
                e.value = null;
            } else {
                int h = k.threadLocalHashCode & (newLen - 1);
                while (newTab[h] != null)
                    h = nextIndex(h, newLen);
                newTab[h] = e;
                count++;
            }
        }
    }

    setThreshold(newLen);
    size = count;
    table = newTab;
}
```

### `ThreadLocalMap.get()`

`set()``set``get()`

#### `ThreadLocalMap.get()`

**** `key``slot``slot``Entry.key``key`

![](./thread-local/26.png)

**** `slot``Entry.key``key`

![](./thread-local/27.png)

`get(ThreadLocal1)``hash``slot`4`index=4``key```ThreadLocal`1`

`index=5``Entry.key=null``expungeStaleEntry()``index 5,8``index 6,7``index = 6``key``Entry`

![](./thread-local/28.png)

#### `ThreadLocalMap.get()`

`java.lang.ThreadLocal.ThreadLocalMap.getEntry()`:

```java
private Entry getEntry(`ThreadLocal` key) {
    int i = key.threadLocalHashCode & (table.length - 1);
    Entry e = table[i];
    if (e != null && e.get() == key)
        return e;
    else
        return getEntryAfterMiss(key, i, e);
}

private Entry getEntryAfterMiss(`ThreadLocal` key, int i, Entry e) {
    Entry[] tab = table;
    int len = tab.length;

    while (e != null) {
        `ThreadLocal` k = e.get();
        if (k == key)
            return e;
        if (k == null)
            expungeStaleEntry(i);
        else
            i = nextIndex(i, len);
        e = tab[i];
    }
    return null;
}
```


### `ThreadLocalMap`key


`ThreadLocalMap`**(expungeStaleEntry())****(cleanSomeSlots())**

`Entry` `null`****

**Heuristically scan some cells looking for stale entries**.

![](./thread-local/29.png)



```java
private boolean cleanSomeSlots(int i, int n) {
    boolean removed = false;
    Entry[] tab = table;
    int len = tab.length;
    do {
        i = nextIndex(i, len);
        Entry e = tab[i];
        if (e != null && e.get() == null) {
            n = len;
            removed = true;
            i = expungeStaleEntry(i);
        }
    } while ( (n >>>= 1) != 0);
    return removed;
}
```

### `InheritableThreadLocal`

`ThreadLocal`

JDK`InheritableThreadLocal`

```java
public class InheritableThreadLocalDemo {
    public static void main(String[] args) {
        ThreadLocal ThreadLocal = new ThreadLocal();
        ThreadLocal inheritableThreadLocal = new InheritableThreadLocal();
        ThreadLocal.set(":threadLocal");
        inheritableThreadLocal.set(":inheritableThreadLocal");

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("`ThreadLocal`" + `ThreadLocal`.get());
                System.out.println("inheritableThreadLocal" + inheritableThreadLocal.get());
            }
        }).start();
    }
}
```



```java
`ThreadLocal`null
inheritableThreadLocal:inheritableThreadLocal
```

`new Thread()``Thread#init``Thread``init`

```java
private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }

    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =
            ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    this.stackSize = stackSize;
    tid = nextThreadID();
}
```

`InheritableThreadLocal``InheritableThreadLocal``new Thread``init()`

`TransmittableThreadLocal`

### `ThreadLocal`

#### `ThreadLocal`

`ELK+Logstash``Kibana`

traceId`traceId`

`org.slf4j.MDC``ThreadLocal`

**A****A**`UUID``traceId``ThreadLocal`**B**`traceId``Header`**B**`Header``traceId``ThreadLocal`

![](./thread-local/30.png)

`requestId``traceId``requestId`

![](./thread-local/31.png)



#### Feign

****
```java
@Component
@Slf4j
public class FeignInvokeInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate template) {
        String requestId = MDC.get("requestId");
        if (StringUtils.isNotBlank(requestId)) {
            template.header("requestId", requestId);
        }
    }
}
```

****
```java
@Slf4j
@Component
public class LogInterceptor extends HandlerInterceptorAdapter {

    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) {
        MDC.remove("requestId");
    }

    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) {
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String requestId = request.getHeader(BaseConstant.REQUEST_ID_KEY);
        if (StringUtils.isBlank(requestId)) {
            requestId = UUID.randomUUID().toString().replace("-", "");
        }
        MDC.put("requestId", requestId);
        return true;
    }
}
```

#### requestId

`MDC``ThreadLocal``ThreadLocal``run()`

```java
public class MyThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
    
    @Override
    public void execute(Runnable runnable) {
        Map context = MDC.getCopyOfContextMap();
        super.execute(() -> run(runnable, context));
    }

    @Override
    private void run(Runnable runnable, Map context) {
        if (context != null) {
            MDC.setContextMap(context);
        }
        try {
            runnable.run();
        } finally {
            MDC.remove();
        }
    }
}
```

#### MQ

MQ`requestId``requestId`



Web Proxy Viewer  |  New URL  |  Original Page