热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

引言无论实际项目实战还是面试,ThreadLocal都是一个绕不开的话题,本文主要从源码角度和大家一起探讨下ThreadLocal的神秘面纱。Thr

引言

无论实际项目实战还是面试,ThreadLocal都是一个绕不开的话题,本文主要从源码角度和大家一起探讨下ThreadLocal的神秘面纱。

  • ThreadLocal是什么?它能干什么?
  • ThreadLocal源码分析
  • 总结



一、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 &#61; v;}}...
}

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

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

private Entry getEntry(ThreadLocal<?> key) {int i &#61; key.threadLocalHashCode & (table.length - 1);Entry e &#61; table[i];if (e !&#61; null && e.get() &#61;&#61; key)return e;elsereturn getEntryAfterMiss(key, i, e);}

private void set(ThreadLocal<?> key, Object value) {// We don&#39;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 &#61; table;int len &#61; tab.length;// 根据 ThreadLocal 的散列值&#xff0c;查找对应元素在数组中的位置int i &#61; key.threadLocalHashCode & (len-1);// 采用“线性探测法”&#xff0c;寻找合适位置for (Entry e &#61; tab[i];e !&#61; null;e &#61; tab[i &#61; nextIndex(i, len)]) {ThreadLocal<?> k &#61; e.get();// 若key 存在&#xff0c;直接覆盖if (k &#61;&#61; key) {e.value &#61; value;return;}// key &#61;&#61; null&#xff0c;但是存在值&#xff08;因为此处的e !&#61; null&#xff09;&#xff0c;说明之前的ThreadLocal对象已经被回收了if (k &#61;&#61; null) {// 用新元素替换陈旧的元素replaceStaleEntry(key, value, i);return;}}//创建新元素tab[i] &#61; new Entry(key, value);int sz &#61; &#43;&#43;size;// 如果没有清理陈旧的 Entry 并且数组中的元素大于了阈值&#xff0c;则进行 rehashif (!cleanSomeSlots(i, sz) && sz >&#61; threshold)rehash();}

2、核心方法解析
(1) get()
返回此线程局部变量的当前线程副本中的值

public T get() {//获取当前线程Thread t &#61; Thread.currentThread();//获取当前线程的成员变量 threadLocalMapThreadLocalMap map &#61; getMap(t);if (map !&#61; null) {// 从当前线程的ThreadLocalMap获取相对应的EntryThreadLocalMap.Entry e &#61; map.getEntry(this);if (e !&#61; null) {&#64;SuppressWarnings("unchecked")// 获取目标值T result &#61; (T)e.value;return result;}}return setInitialValue();}

首先通过当前线程获取所对应的成员变量ThreadLocalMap&#xff0c;然后通过ThreadLocalMap获取当前ThreadLocalEntry&#xff0c;最后通过所获取的Entry获取目标值result

(2) set(T value)
将此线程局部变量的当前线程副本中的值设置为指定值。

public void set(T value) {Thread t &#61; Thread.currentThread();ThreadLocalMap map &#61; getMap(t);if (map !&#61; null)map.set(this, value);elsecreateMap(t, value);}

获取当前线程所对应的ThreadLocalMap&#xff0c;如果不为空&#xff0c;则调用ThreadLocalMapset()方法&#xff0c;key就是当前ThreadLocal&#xff0c;如果不存在&#xff0c;则调用createMap()方法新建一个。

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

(3) initialValue()
返回此线程局部变量的当前线程的初始值。

protected T initialValue() {return null;}

该方法定义为protected级别且返回为null&#xff0c;需要其子类实现其功能&#xff0c;所以我们在使用ThreadLocal的时候一般都应该覆盖该方法。该方法不能显示调用&#xff0c;只有在第一次调用get()或者set()方法时才会被执行&#xff0c;并且仅执行1次。

(4) remove()
移除此线程局部变量当前线程的值。

public void remove() {ThreadLocalMap m &#61; getMap(Thread.currentThread());if (m !&#61; null)m.remove(this);}



三、总结

1、多个线程去获取一个共享变量时&#xff0c;要求获取的是这个变量的初始值的副本。每个线程存储这个变量的副本&#xff0c;对这个变量副本的改变不会影响变量本身。适用于多个线程依赖不同变量值完成操作的场景。比如&#xff1a;

  • 多数据源的切换
  • spring声明式事务

2、将ThreadLocal设置成private static的&#xff0c;这样ThreadLocal会尽量和线程本身一起回收。


推荐阅读
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • 开发笔记:Java是如何读取和写入浏览器Cookies的
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java是如何读取和写入浏览器Cookies的相关的知识,希望对你有一定的参考价值。首先我 ... [详细]
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • HashMap的相关问题及其底层数据结构和操作流程
    本文介绍了关于HashMap的相关问题,包括其底层数据结构、JDK1.7和JDK1.8的差异、红黑树的使用、扩容和树化的条件、退化为链表的情况、索引的计算方法、hashcode和hash()方法的作用、数组容量的选择、Put方法的流程以及并发问题下的操作。文章还提到了扩容死链和数据错乱的问题,并探讨了key的设计要求。对于对Java面试中的HashMap问题感兴趣的读者,本文将为您提供一些有用的技术和经验。 ... [详细]
  • 从相邻元素对还原数组的解题思路和代码
    本文介绍了从相邻元素对还原数组的解题思路和代码。思路是使用HashMap存放邻接关系,并找出起始点,然后依次取。代码使用了HashMap来存放起始点所在的adjacentPairs中的位置,并对重复的起始点进行处理。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • 本文整理了Java中org.apache.solr.common.SolrDocument.setField()方法的一些代码示例,展示了SolrDocum ... [详细]
  • 第七课主要内容:多进程多线程FIFO,LIFO,优先队列线程局部变量进程与线程的选择线程池异步IO概念及twisted案例股票数据抓取 ... [详细]
  • C语言的经典程序有哪些
    本篇内容介绍了“C语言的经典程序有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何 ... [详细]
  • Java编程思想一书中第21章并发中关于线程间协作的一节中有个关于汽车打蜡与抛光的小例子(原书的704页)。这个例子主要展示的是两个线程如何通过wait ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • ejava,刘聪dejava
    本文目录一览:1、什么是Java?2、java ... [详细]
  • 1Lock与ReadWriteLock1.1LockpublicinterfaceLock{voidlock();voidlockInterruptibl ... [详细]
author-avatar
chnger
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有