Java并发编程系列之六:深入理解ThreadLocal


ThreadLocal 是一个线程的本地变量, 也就意味着这个变量是线程独有的,是不能与其他线程共享的,它并不是解决多线程共享变量的问题。

所以ThreadLocal与线程同步机制不同,线程同步机制是多个线程共享同一个变量,而ThreadLocal是为每一个线程创建一个单独的变量副本,故而每个线程都可以独立地改变自己所拥有的变量副本,而不会影响其他线程所对应的副本。可以说ThreadLocal为多线程环境下变量问题提供了另外一种解决思路。

ThreadLocal的思想就是用空间换时间,使各线程都能访问属于自己这一份的变量副本,变量值不互相干扰,减少同一个线程内的多个函数或者组件之间一些公共变量传递的复杂度。

在这里插入图片描述


二、ThreadLocal源码分析


1、ThreadLocalMap解析

ThreadLocal内部定义了一个ThreadLocalMap的内部类,ThreadLocalMap实际利用Entry来实现key-value的存储,如下所示:

static class ThreadLocalMap {

static class Entry extends WeakReference<ThreadLocal<?>> {

/** The value associated with this ThreadLocal. */

Object value;

Entry(ThreadLocal<?> k, Object v) {

super(k);

value = v;

}

}

}

ThreadLocalMap是实现线程隔离机制的关键,从以上代码可以看出Entrykey就是ThreadLocal,而value就是值。同时,Entry也继承WeakReference,所以说Entry所对应keyThreadLocal实例)的引用为一个弱引用。

我们主要来看下核心的getEntry()set(ThreadLocal> key, Object value)方法

private Entry getEntry(ThreadLocal<?> key) {

int i = key.threadLocalHashCode & (table.length - 1);

Entry e = table[i];

if (e != null && e.get() == key)

return e;

else

return getEntryAfterMiss(key, i, e);

}

private void set(ThreadLocal<?> key, Object value) {

// We don’t use a fast path as with get() because it is at

// least as common to use set() to create new entries as

// it is to replace existing ones, in which case, a fast

// path would fail more often than not.

Entry[] tab = table;

int len = tab.length;

// 根据 ThreadLocal 的散列值,查找对应元素在数组中的位置

int i = key.threadLocalHashCode & (len-1);

// 采用“线性探测法”,寻找合适位置

for (Entry e = tab[i];

e != null;

e = tab[i = nextIndex(i, len)]) {

ThreadLocal<?> k = e.get();

// 若key 存在,直接覆盖

if (k == key) {

e.value = value;

return;

}

// key == null,但是存在值(因为此处的e != null),说明之前的ThreadLocal对象已经被回收了

if (k == null) {

// 用新元素替换陈旧的元素

replaceStaleEntry(key, value, i);

return;

}

}

//创建新元素

tab[i] = new Entry(key, value);

int sz = ++size;

// 如果没有清理陈旧的 Entry 并且数组中的元素大于了阈值,则进行 rehash

if (!cleanSomeSlots(i, sz) && sz >= threshold)

rehash();

}

2、核心方法解析

(1) get()

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

public T get() {

//获取当前线程

Thread t = Thread.currentThread();

//获取当前线程的成员变量 threadLocalMap

ThreadLocalMap map = getMap(t);

if (map != null) {

// 从当前线程的ThreadLocalMap获取相对应的Entry

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

if (e != null) {

@SuppressWarnings(“unchecked”)

// 获取目标值

T result = (T)e.value;

return result;

}

}

return setInitialValue();

}

最后

分享一些资料给大家,我觉得这些都是很有用的东西,大家也可以跟着来学习,查漏补缺。

《Java高级面试》

《Java高级架构知识》

《算法知识》

最后

分享一些资料给大家,我觉得这些都是很有用的东西,大家也可以跟着来学习,查漏补缺。

《Java高级面试》

[外链图片转存中…(img-QltWF8TG-1714366604945)]

《Java高级架构知识》

[外链图片转存中…(img-TX4sALfo-1714366604946)]

《算法知识》

[外链图片转存中…(img-ypfNY0wu-1714366604947)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值