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

**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```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
```

`ThreadLocal`
```java
new ThreadLocal().set(s);
```
`GC``key``debug``referent=null`, ****

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

**** `key` `value` `key` `value`
### `ThreadLocal.set()`

`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` ****

****
### `ThreadLocalMap` Hash
> **** ****`Entry`********`Entry``key``null`********`Entry``null`
`ThreadLocalMap`****`hash``Hash`
`HashMap`********
`ThreadLocalMap``HashMap`

`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`

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

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

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

7`Entry``key``null``key``replaceStaleEntry()`******index=7**
`slotToExpunge = staleSlot = 7`
`staleSlot` `slotToExpunge``for``Entry``null`
`Entry=null`**slotToExpunge0**

(`index=7`)`Entry``slotToExpunge``null``slotToExpunge`0
`slotToExpunge``staleSlot`
`staleSlot`(index=7)**keyEntry**

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

**keyEntry**

`staleSlot``key``Entry``Entry``null``table``key``Entry`
`Entry``table[stableSlot]`

`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()`

```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`

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

`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()`

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

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

index=4index=3
`slot`

****
```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`

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

`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`

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

`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`

#### `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**.

```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`

`requestId``traceId``requestId`

#### 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`