文章
- https://zhuanlan.zhihu.com/p/99150038
- https://zhuanlan.zhihu.com/p/61587053
- https://www.zhihu.com/question/341005993/answer/1367225682
的学习笔记
ThreadLocal中填充的变量属于当前线程,与其他线程独立。ThreadLocal为变量在每个线程中都创建了一个副本,每个线程访问自己内部的变量
使用方式👇
1 | public class ThreadLocalTest01 { |
源码分析
set()
方法
1 | public void set(T value) { |
显然需要先搞清楚ThreadLocalMap
是什么
1 | /** |
然后是get()
源码
1 | /** |
再来看setInitialValue()
方法
1 | private T setInitialValue() { |
另外还有remove()
方法
1 | public void remove() { |
通过查看源码可以总结出Thread
、ThreadLocal
、ThreadLocalMap
三者的关系:
ThreadLocalMap:ThreadLocal->Object
是在Thread
类内持有的,这非常合理,因为一个线程内很可能有多个ThreadLocal
变量- ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value
.
内存泄漏问题
ThreadLocal是一个弱引用
只具有弱引用的对象拥有更短暂的生命周期:在GC线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间是否足够,都会回收它的内存
不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象
WeakReference引用本身是强引用,它内部的(T reference)才是真正的弱引用字段,WeakReference只是一个装弱引用的容器而已
于是会出现一个现象:key为null,但value还存在
当线程一直存在(比如线程池),后续继续使用这个线程,ThreadLocalMap会不断增大,继而发生内存泄漏
所以,使用完ThreadLocal
后,需要调用remove()
方法
ThreadLocalMap
可以发现,set()
、get()
、remove()
的底层核心逻辑其实在ThreadLocalMap
中,这是一个独立的类,并不继承任何类,但是自己实现了HashMap的功能
1 | static class ThreadLocalMap { |
但是它当中并没有链表,那么如何解决哈希冲突呢?
我们来看它的set()
方法
1 | private void set(ThreadLocal<?> key, Object value) { |
可以很明显的看到,ThreadLocalMap
使用线性探测法解决哈希冲突,下面是getEntry()
的源码
1 | private Entry getEntry(ThreadLocal<?> key) { |
InheritableThreadLocal
1 | private void test() { |
在主线程中创建InheritableThreadLocal实例,然后在子线程中得到设置的值,实现变量共享
底层原理:
1 | public class Thread implements Runnable{ |
应用场景
Spring中使用ThreadLocal实现事务隔离:
1
2
3
4
5
6
7
8
9
10
11public abstract class TransactionSynchronizationManager {
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<>("Transaction synchronizations");
private static final ThreadLocal<String> currentTransactionName =
new NamedThreadLocal<>("Current transaction name");
...
}Spring Security中实现SecurityContextHolderStrategy接口
SecurityContextHolder.initializeStrategy()👇
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20private static void initializeStrategy() {
...
if (!StringUtils.hasText(strategyName)) {
// Set default
strategyName = MODE_THREADLOCAL;
}
if (strategyName.equals(MODE_THREADLOCAL)) {
strategy = new ThreadLocalSecurityContextHolderStrategy();
return;
}
if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {
strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
return;
}
if (strategyName.equals(MODE_GLOBAL)) {
strategy = new GlobalSecurityContextHolderStrategy();
return;
}
...
}SecurityContextHolderStrategy接口共有四种实现类,其中ThreadLocalSecurityContextHolderStrategy和InheritableThreadLocalSecurityContextHolderStrategy中分别封装了ThreadLocal和InheritableThreadLocal对象,以ThreadLocalSecurityContextHolderStrategy为例👇
1
2
3
4final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<>();
...
}Andriod中使用ThreadLocal保证只有一个
Looper
对象1
2
3
4
5
6
7static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}使用ThreadLocal实现过渡传参
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21//修改前
void work(User user) {
getInfo(user);
checkInfo(user);
setSomeThing(user);
log(user);
}
//修改后
void work(User user) {
try{
threadLocalUser.set(user);
// 他们内部 User u = threadLocalUser.get(); 就好了
getInfo();
checkInfo();
setSomeThing();
log();
} finally {
threadLocalUser.remove();
}
}