前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JDK源码解析之 java.lang.ThreadLocal

JDK源码解析之 java.lang.ThreadLocal

作者头像
栗筝i
发布2022-12-01 20:12:13
2270
发布2022-12-01 20:12:13
举报
文章被收录于专栏:迁移内容迁移内容

此类提供线程局部变量。这些变量与普通变量不同,每个访问一个线程(通过其getset方法)的线程 都有其自己的,独立初始化的变量副本。 ThreadLocal实例通常是希望将状态与线程关联的类中的私有静态字段(例如,用户ID或事务ID)。

以射击游戏举例,游戏开始时,每个人能够领到一把枪,枪把上有三个数字:子弹数、杀敌数、自己的命数,为其设置的初始值分别为100、0、10.设战场上的每个人都是一个线程,那么这三个初始值写在哪里呢? 如果每个线程都写死这三个值,万一将初始子弹数统一改成 1000发呢? 如果共享,那么线程之间的并发修改会导致数据不准确. 能不能构造这样一个对象,将这个对象设置为共享变量,统一设置初始值,但是每个线程对这个值的修改都是互相独立的.这个对象就是ThreadLocal

一、类定义

代码语言:javascript
复制
public class ThreadLocal<T> {}

…用来限制Class中的参数类型,确保Class中参数的一致性

二、实例变量和相关方法

代码语言:javascript
复制
//用于ThreadLocalMap
private final int threadLocalHashCode = nextHashCode();

//下一个hash code,从0开始
private static AtomicInteger nextHashCode = new AtomicInteger();

//hash增量
private static final int HASH_INCREMENT = 0x61c88647;

//在获取下一个hash code
private static int nextHashCode() {
    return nextHashCode.getAndAdd(HASH_INCREMENT);
}

三、内部类

内部类:ThreadLocalMap

ThreadLocalMap负责存储ThreadLocal及其变量,即ThreadLocal对象本身作为键,ThreadLocal存储的变量作为值。每个Thread对象在声明一个ThreadLocal后会持有一个ThreadLocalMap的引用,来实现ThreadLocal的功能。

ThreadLocalMap持有一个内部类Entry,类似于HashMap.Node类,负责保存键值对。

代码语言:javascript
复制
static class Entry extends WeakReference<ThreadLocal<?>> {
	Object value;
	Entry(ThreadLocal<?> k, Object v) {
		super(k);
		value = v;
	}
}

Entry继承了WeakReference类,使Entry的键为弱引用。

看到这里很多人或许有这样一个疑问:为什么Entry要继承WeakReference? 既然将ThreadLocal声明为弱引用,那么自然会联想到和GC有关。

如果不声明为弱引用,以最上面Test类的代码为例,当我们将上述ThreadLocal类型的静态变量tl设置为null时,Thread对象成员变量threadLocals依然保留有一个ThreadLocalMap,该Map中持有保存该ThreadLocalEntry,在这个线程运行期间无法GC,从而引发内存泄漏。所以,Entry的键要声明为弱引用。

四、主要方法

1.get()

返回此线程局部变量的当前线程副本中的值。

代码语言:javascript
复制
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();
}

首先是获取当前运行线程对象,然后根据该线程对象找到对应的ThreadLocalMap

  • 如果找到了该线程对应的ThreadLocalMap,则通过当前ThreadLocal对象作为键查找Map中对应的Entry(键值对)对象
  • 如果查找结果不为null,则返回Entry对象的value。否则调用setInitialValue方法将当前ThreadLocal对象(this)和变量作为键值对存入ThreadLocalMap并返回变量。
2.initialValue()

为变量设置初始值,该方法的默认实现是:

代码语言:javascript
复制
protected T initialValue() {
	return null;
}

如果想要为该变量设置一个初始值,只需重写该方法即可,例如:

代码语言:javascript
复制
@Override
protected String initialValue() {
	return "hello world";
}
3.set(T value)

get方法类似,set方法首先会获取当前运行的Thread对象并通过该对象获取对应的ThreadLocalMap,如果map为空,则为当前Thread对象新建一个ThreadLocalMap,否则直接将value放入map中。

代码语言:javascript
复制
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
	if (map != null)
		map.set(this, value);
	else
		createMap(t, value);
}
4、remove()

同样,获取当前Thread对应的ThreadLocalMap,然后调用ThreadLocalMapremove方法移除ThreadLocal对象,无需通过弱引用机制对该ThreadLocal对象进行GC。

代码语言:javascript
复制
public void remove() {
	ThreadLocalMap m = getMap(Thread.currentThread());
	if (m != null)
		m.remove(this);
}

五、总结

ThreadLocal是如何做到为每一个线程维护变量的副本的呢?

ThreadLocal类中设置了一个Map,存储每一个线程的变量的副本。

ThreadLocal使用场合主要解决多线程中数据数据因并发产生不一致问题。ThreadLocal为每个线程的中并发访问的数据提供一个副本,通过访问副本来运行业务,这样的结果是耗费了内存,单大大减少了线程同步所带来性能消耗,也减少了线程并发控制的复杂度。

Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。

Threadlocal底层是通过threadlocalMap进行存储键值 每个ThreadLocal类创建一个Map,然后用线程的ID作为Map的key,实例对象作为Map的value,这样就能达到各个线程的值隔离的效果。 ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-08-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、类定义
  • 二、实例变量和相关方法
  • 三、内部类
    • 内部类:ThreadLocalMap
    • 四、主要方法
      • 1.get()
        • 2.initialValue()
          • 3.set(T value)
            • 4、remove()
            • 五、总结
              • ThreadLocal是如何做到为每一个线程维护变量的副本的呢?
              相关产品与服务
              对象存储
              对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档