社区编辑申请
注册/登录
聊聊Java中的ThreadLocal作用
开发 前端
使用比较简单,通过重载initialValue()这个方法进行初始化,或通过set进行设置,然后get使用即可,整个使用过程类似于HashMap。

在java中,如果我们多线程操作变量的时候,需要加上同步控制机制,原因是多线程操作一个变量,那么如果每个线程都操作自己线程的变量,那就不用加锁了,也不用加同步控制了。

ThreadLocal就是这个作用,比如在Web开发中,我们用ThreadLocal来保存用户信息,然后传递后台多个service,然后每个线程单独获取自己的用户信息;

初始化代码也比较简单:

public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("mm:ss");
};

使用比较简单,通过重载initialValue()这个方法进行初始化,或通过set进行设置,然后get使用即可,整个使用过程类似于HashMap。

那如何神奇的控制不同的线程保存不同的数据,从而达到线程的共享那,如下:

public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}

首先代码中通过Thread.currentThread()来获取当前的线程id,通过线程id获取对应的ThreadLocalMap,这个getMap,其实是获取Thread的成员变量如下:

ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}

此成员变量定义如下:

ThreadLocal.ThreadLocalMap threadLocals = null;

然后再来看这句话:

ThreadLocalMap.Entry e = map.getEntry(this);

即通过本ThreadLocal的对象作为key,获取Entry对象后,再获取它的value,如果为null那,那就调用setInitialValue()进行初始化,代码如下:

private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}

此线程的map如果存在,不为null,直接更新,返回默认的初始化值,即initialValue()的返回值,如果不存在,则调用createMap(t,value);来创建map,如下:

void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}

说实话代码挺绕的,找了网上一张图,会形成如下的结构:

【图来自互联网,侵权删除】

我们从这个图可以看到,ThreadLocal是所有线程的map的公共key,还要注意到,这个map比较特殊,是内部自己实现的,通过线性探测的方法来解决哈希冲突的,即如果槽位已经被占用了,则通过一个函数计算得到下一个槽位, 这种方法解决冲突的效率比较低,所以不建议用太多的ThreadLocal变量。

Threadlocal相关的数据结构:

【图片来自互联网,侵权删除】

【图片来自互联网,侵权删除】

从上图中可以看到Entry继承自弱应用,下次gc的时候会回收,但是只有key是弱引用,value还是强引用,下次gc的时候,key被回收而value可能一直不会被回收。

static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;

Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}

所以解决办法是,使用过之后记得通过remove()进行删除。

总结:

ThreadLocal适用于无状态的线程内变量共享的场景,比如我们说的通过ThreadLocal保存每个线程特有的信息,比如线程标识(打日志的时候适用,便于排查问题)。

ThreadLocal有一定的内存泄漏分享,记得要remove。

责任编辑:武晓燕 来源: 今日头条
相关推荐

2022-06-05 21:09:47

Python办公自动化

2022-06-29 09:19:09

静态代码C语言c代码

2022-06-10 17:37:37

数据库

2022-06-01 11:14:42

Java代码技巧

2022-06-27 09:54:38

编程语言JavaC++

2022-05-11 07:36:12

Java线程安全

2022-05-23 11:03:53

云原生技术DockerIstio

2022-06-13 07:36:06

2022-06-27 07:32:00

2022-06-13 12:43:13

Java模块

2022-05-24 07:36:53

Java 8APIJava

2022-06-30 09:07:52

2022-06-28 10:58:21

工具Java

2022-05-24 08:21:16

数据安全API

2022-06-08 13:48:06

物联网卡智能设备SIM卡

2022-06-21 09:02:49

python技巧

2022-06-16 14:07:26

Java代码代码review

2022-04-01 08:27:30

告警收敛运维监控

2022-06-22 08:02:11

2022-06-26 09:55:00

接口自动化项目

同话题下的热门内容

手把手教你用装饰器扩展 Python 计时器IOC-Golang 的 AOP 原理与应用Vue 里,多级菜单要如何设计才显得专业?分析了 700 万份工作需求,市场需求最高的八种编程语言是这些Vue 2.7 正式发布,代号为 Naruto手把手教你实现一个 Python 计时器2022 年编程语言趋势:Swift、Kotlin 热度持续增长,收入最高的五种语言竟是它们分布式事务(Seata) 四大模式详解

编辑推荐

太厉害了,终于有人能把TCP/IP协议讲的明明白白了!牛人5次面试腾讯不成功的经验HBase原理–所有Region切分的细节都在这里了Javascript如何监听页面刷新和关闭事件如何搭建一个HTTPS服务端
我收藏的内容
点赞
收藏

51CTO技术栈公众号