Skip to content

Commit 6ee3db6

Browse files
author
hailong.sha
committed
Update 万字详解ThreadLocal关键字.md
formation update
1 parent 6f73cee commit 6ee3db6

File tree

1 file changed

+19
-20
lines changed

1 file changed

+19
-20
lines changed

docs/java/multi-thread/万字详解ThreadLocal关键字.md

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
public class ThreadLocalTest {
3636
private List<String> messages = Lists.newArrayList();
3737

38-
public static final `ThreadLocal`<ThreadLocalTest> holder = `ThreadLocal`.withInitial(ThreadLocalTest::new);
38+
public static final ThreadLocal<ThreadLocalTest> holder = ThreadLocal.withInitial(ThreadLocalTest::new);
3939

4040
public static void add(String message) {
4141
holder.get().messages.add(message);
@@ -70,20 +70,19 @@ size: 0
7070

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

73-
74-
`Thread`类有一个类型为``ThreadLocal`.`ThreadLocalMap``的实例变量`threadLocals`,也就是说每个线程有一个自己的`ThreadLocalMap`
73+
`Thread`类有一个类型为`ThreadLocal.ThreadLocalMap`的实例变量`threadLocals`,也就是说每个线程有一个自己的`ThreadLocalMap`。
7574

7675
`ThreadLocalMap`有自己的独立实现,可以简单地将它的`key`视作`ThreadLocal`,`value`为代码中放入的值(实际上`key`并不是`ThreadLocal`本身,而是它的一个**弱引用**)。
7776

7877
每个线程在往`ThreadLocal`里放值的时候,都会往自己的`ThreadLocalMap`里存,读也是以`ThreadLocal`作为引用,在自己的`map`里找对应的`key`,从而实现了**线程隔离**
7978

8079
`ThreadLocalMap`有点类似`HashMap`的结构,只是`HashMap`是由**数组+链表**实现的,而`ThreadLocalMap`中并没有**链表**结构。
8180

82-
我们还要注意`Entry`, 它的`key`是``ThreadLocal`<?> k` ,继承自`WeakReference, 也就是我们常说的弱引用类型。
81+
我们还要注意`Entry`, 它的`key`是`ThreadLocal<?> k` ,继承自`WeakReference`, 也就是我们常说的弱引用类型。
8382

8483
### GC 之后key是否为null
8584

86-
回应开头的那个问题, `ThreadLocal``key`是弱引用,那么在` `ThreadLocal`.get()`的时候,发生`GC`之后,`key`是否是`null`
85+
回应开头的那个问题, `ThreadLocal` 的`key`是弱引用,那么在`ThreadLocal.get()`的时候,发生`GC`之后,`key`是否是`null`?
8786

8887
为了搞清楚这个问题,我们需要搞清楚`Java`的**四种引用类型**
8988

@@ -110,19 +109,19 @@ public class ThreadLocalDemo {
110109

111110
private static void test(String s,boolean isGC) {
112111
try {
113-
new `ThreadLocal`<>().set(s);
112+
new ThreadLocal<>().set(s);
114113
if (isGC) {
115114
System.gc();
116115
}
117116
Thread t = Thread.currentThread();
118117
Class<? extends Thread> clz = t.getClass();
119118
Field field = clz.getDeclaredField("threadLocals");
120119
field.setAccessible(true);
121-
Object `ThreadLocalMap` = field.get(t);
122-
Class<?> tlmClass = `ThreadLocalMap`.getClass();
120+
Object ThreadLocalMap = field.get(t);
121+
Class<?> tlmClass = ThreadLocalMap.getClass();
123122
Field tableField = tlmClass.getDeclaredField("table");
124123
tableField.setAccessible(true);
125-
Object[] arr = (Object[]) tableField.get(`ThreadLocalMap`);
124+
Object[] arr = (Object[]) tableField.get(ThreadLocalMap);
126125
for (Object o : arr) {
127126
if (o != null) {
128127
Class<?> entryClass = o.getClass();
@@ -142,8 +141,8 @@ public class ThreadLocalDemo {
142141

143142
结果如下:
144143
```java
145-
弱引用key:java.lang.`ThreadLocal`@433619b6,值:abc
146-
弱引用key:java.lang.`ThreadLocal`@418a15e3,值:java.lang.ref.SoftReference@bf97a12
144+
弱引用key:java.lang.ThreadLocal@433619b6,值:abc
145+
弱引用key:java.lang.ThreadLocal@418a15e3,值:java.lang.ref.SoftReference@bf97a12
147146
--gc后--
148147
弱引用key:null,值:def
149148
```
@@ -162,7 +161,7 @@ new ThreadLocal<>().set(s);
162161

163162
这个问题刚开始看,如果没有过多思考,**弱引用**,还有**垃圾回收**,那么肯定会觉得是`null`。
164163

165-
其实是不对的,因为题目说的是在做 ``ThreadLocal`.get()` 操作,证明其实还是有**强引用**存在的,所以 `key` 并不为 `null`,如下图所示,`ThreadLocal`**强引用**仍然是存在的。
164+
其实是不对的,因为题目说的是在做 `ThreadLocal.get()` 操作,证明其实还是有**强引用**存在的,所以 `key` 并不为 `null`,如下图所示,`ThreadLocal`的**强引用**仍然是存在的。
166165

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

@@ -217,8 +216,8 @@ public class ThreadLocal<T> {
217216
return nextHashCode.getAndAdd(HASH_INCREMENT);
218217
}
219218

220-
static class `ThreadLocalMap` {
221-
`ThreadLocalMap`(`ThreadLocal`<?> firstKey, Object firstValue) {
219+
static class ThreadLocalMap {
220+
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
222221
table = new Entry[INITIAL_CAPACITY];
223222
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
224223

@@ -230,7 +229,7 @@ public class ThreadLocal<T> {
230229
}
231230
```
232231

233-
每当创建一个`ThreadLocal`对象,这个``ThreadLocal`.nextHashCode` 这个值就会增长 `0x61c88647`
232+
每当创建一个`ThreadLocal`对象,这个`ThreadLocal.nextHashCode` 这个值就会增长 `0x61c88647` 。
234233

235234
这个值很特殊,它是**斐波那契数** 也叫 **黄金分割数**。`hash`增量为 这个数字,带来的好处就是 `hash` **分布非常均匀**
236235

@@ -244,7 +243,7 @@ public class ThreadLocal<T> {
244243

245244
> **注明:** 下面所有示例图中,**绿色块**`Entry`代表**正常数据****灰色块**代表`Entry`的`key`值为`null`,**已被垃圾回收****白色块**表示`Entry`为`null`。
246245

247-
虽然`ThreadLocalMap`中使用了**黄金分隔数来**作为`hash`计算因子,大大减少了`Hash`冲突的概率,但是仍然会存在冲突。
246+
虽然`ThreadLocalMap`中使用了**黄金分割数来**作为`hash`计算因子,大大减少了`Hash`冲突的概率,但是仍然会存在冲突。
248247

249248
`HashMap`中解决冲突的方法是在数组上构造一个**链表**结构,冲突的数据挂载到链表上,如果链表长度超过一定数量则会转化成**红黑树**
250249

@@ -403,7 +402,7 @@ private static int prevIndex(int i, int len) {
403402
`java.lang.ThreadLocal.ThreadLocalMap.replaceStaleEntry()`:
404403

405404
```java
406-
private void replaceStaleEntry(`ThreadLocal`<?> key, Object value,
405+
private void replaceStaleEntry(ThreadLocal<?> key, Object value,
407406
int staleSlot) {
408407
Entry[] tab = table;
409408
int len = tab.length;
@@ -687,7 +686,7 @@ private void resize() {
687686

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

690-
我们以`get(ThreadLocal1)`为例,通过`hash`计算后,正确的`slot`位置应该是4,而`index=4`的槽位已经有了数据,且`key`值不等于``ThreadLocal`1`,所以需要继续往后迭代查找。
689+
我们以`get(ThreadLocal1)`为例,通过`hash`计算后,正确的`slot`位置应该是4,而`index=4`的槽位已经有了数据,且`key`值不等于`ThreadLocal1`,所以需要继续往后迭代查找。
691690

692691
迭代到`index=5`的数据时,此时`Entry.key=null`,触发一次探测式数据回收操作,执行`expungeStaleEntry()`方法,执行完后,`index 5,8`的数据都会被回收,而`index 6,7`的数据都会前移,此时继续往后迭代,到`index = 6`的时候即找到了`key`值相等的`Entry`数据,如下图所示:
693692

@@ -698,7 +697,7 @@ private void resize() {
698697
`java.lang.ThreadLocal.ThreadLocalMap.getEntry()`:
699698

700699
```java
701-
private Entry getEntry(`ThreadLocal`<?> key) {
700+
private Entry getEntry(ThreadLocal<?> key) {
702701
int i = key.threadLocalHashCode & (table.length - 1);
703702
Entry e = table[i];
704703
if (e != null && e.get() == key)
@@ -707,7 +706,7 @@ private Entry getEntry(`ThreadLocal`<?> key) {
707706
return getEntryAfterMiss(key, i, e);
708707
}
709708

710-
private Entry getEntryAfterMiss(`ThreadLocal`<?> key, int i, Entry e) {
709+
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
711710
Entry[] tab = table;
712711
int len = tab.length;
713712

0 commit comments

Comments
 (0)