毋峻舷 发表于 2025-10-6 12:33:54

浅谈ThreadLocal----每个线程一个小书包

ThreadLocal是什么?
thread是线程,local是本地的意思
字面意思是线程本地。
其实更通俗的理解是给每个线程设置一个缓存。这个缓存用来存储当前线程在未来的业务逻辑中需要执行到的变量。
我们先来看怎么用:
首先创建全局变量ThreadLocal,
各自启动一个线程任务:
线程任务将变量设置到缓存中。
线程任务需要用到缓存中的变量时,直接从缓存中取即可。
1 import java.util.concurrent.TimeUnit;
2
3 /**
4* @discription
5*/
6 public class ThreadLocalLearn {
7   static ThreadLocal<String> threadLocal = new ThreadLocal<>();
8
9   public static void main(String[] args) {
10         Runnable r = new Runnable() {
11             @Override
12             public void run() {
13               threadLocal<strong>.set</strong>(Thread.currentThread().getName());
14               sayMyName();
15               threadLocal.<strong>remove</strong>();
16             }
17
18             public void sayMyName() {
19               for (int i = 0; i < 3; i++) {
20                     String name = threadLocal.<strong>get</strong>();
21                     System.out.println(Thread.currentThread().getName() + " say: im a thread, name:" + name);
22                     try {
23                         TimeUnit.SECONDS.sleep(3);
24                     } catch (Exception e) {
25                         //...
26                     }
27               }
28             }
29         };
30         Thread t1 = new Thread(r);
31         t1.start();
32         Thread t2 = new Thread(r);
33         t2.start();
34   }
35 }它的使用非常简单,
(1)先set()存储值;
(2)使用时get()取出值;
(3)用完了使用remove()清理掉;
输出如下:
Connected to the target VM, address: '127.0.0.1:56863', transport: 'socket'
Thread-0 say: im a thread, name:Thread-0
Thread-1 say: im a thread, name:Thread-1
Thread-0 say: im a thread, name:Thread-0
Thread-1 say: im a thread, name:Thread-1
Thread-1 say: im a thread, name:Thread-1
Thread-0 say: im a thread, name:Thread-0
Disconnected from the target VM, address: '127.0.0.1:56863', transport: 'socket'很多人第一次见到ThreadLocal,第一直觉它的实现是用Map 。(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ )但是深入研究之后,你会发现threadLocal的实现要比这样一个map 精妙的多,也好用的多。
我们通过查看java源码,可以依次探索ThreadLocal是如何实现缓存的:
类整体的关系大概是这样的:

 查看源码,我们可以发现如下特性:
1、ThreadLocal本身并不是缓存,它只是起到一个缓存的key 的作用。我们每次创建一个ThreadLocal 并不是真正的创建了一个缓存,其实只是创建了一个缓存的标识。
源码如下:this 就是ThreadLocal实例
1   public void set(T value) {
2         Thread t = Thread.currentThread();
3         ThreadLocalMap map = getMap(t);
4         if (map != null) {
5             map.set(this, value);
6         } else {
7             createMap(t, value);
8         }
9   }2、真正的缓存保存在Thread中,缓存被定义为:
ThreadLocal.ThreadLocalMap threadLocals;
从名字可以发现,这个缓存的类型是在ThreadLocal 中定义的一个静态内部类。这个类就是用来真正存放缓存的地方。这就像是thread小书包一样,每个线程有一个自己的独立的存储空间。
设计疑问:它(ThreadLocalMap)为什么没有定义在Thread类中,毕竟它是Thread的缓存。
源码如下:Thread.java
1   /* ThreadLocal values pertaining to this thread. This map is maintained
2      * by the ThreadLocal class. */
3   ThreadLocal.ThreadLocalMap threadLocals = null; 3、查看ThreadLocalMap的源码,我们发现它并没有实现Map接口,就像其他map一样,ThreadLocalMap实现了常用的Map中的set,get,getEntry,setThreshold,,remove 等方法。
并且它内部使用了线性探测法来解决哈希冲突。
设计疑问:它(ThreadLocalMap)为什么没有实现Map接口?
源码如下:ThreadLocal.Java
1 static class ThreadLocalMap {
2
3         //...
4
5         private static final int INITIAL_CAPACITY = 16;
6
7
8         private Entry[] table;
9
10
11         private int size = 0;
12
13
14         private int threshold; // Default to 0
15
16
17         private void setThreshold(int len) {
18             threshold = len * 2 / 3;
19         }
20
21
22         private Entry getEntry(ThreadLocal<?> key) {
23                  ...
24         }
25
26
27
28         private void set(ThreadLocal<?> key, Object value) {
29         ...
30         }
31
32
33         private void remove(ThreadLocal<?> key) {
34         ...
35         }
36
37
38         private void rehash() {
39             ...
40         }
41
42         private void resize() {
43            ...
44         }
45       ....
46   }4、继续看源码,我们发现ThreadLocalMap类像其他Map实现一样,在内部定义了Entry。并且这个Entry居然继承了弱引用,弱引用被定义在Entry的key上,而且key的类型是ThreadLocal。
至于什么是弱引用,我以前的文章中介绍过,请看(浅谈Java中的引用   https://www.cnblogs.com/jilodream/p/6181762.html),一定要对弱引用了解,否则ThreadLocal的核心实现以及它会存在的问题,就无法更深理解了。
这里又会有疑问,为什么要使用弱引用,使用强引用不好吗?弱引用万一被回收导致空引用等问题怎么办?

源码如下:ThreadLocal.Java
1         static class Entry extends WeakReference
页: [1]
查看完整版本: 浅谈ThreadLocal----每个线程一个小书包