**

一:ThreadLocal的简要介绍及使用

**

Java中的ThreadLocal类允许我们创建只能被同一个线程读写的变量。因此,如果一段代码含有一个ThreadLocal变量的引用,即使两个线程同时执行这段代码,它们也无法访问到对方的ThreadLocal变量。

ThreadLocal的常见用法:

  • 存储单个线程上下文信息。比如存储id等;
  • 使变量线程安全。变量既然成为了每个线程内部的局部变量,自然就不会存在并发问题了;
  • 减少参数传递。比如做一个trace工具,能够输出工程从开始到结束的整个一次处理过程中所有的信息,从而方便debug。由于需要在工程各处随时取用,可放入ThreadLocal。(如果想要当前线程的子线程共享父线程的变量,可以使用InheritableThreadLocal)

如何创建ThreadLocal变量

以下代码展示了如何创建一个ThreadLocal变量:

private ThreadLocal my = new ThreadLocal();

我们可以看到,通过这段代码实例化了一个ThreadLocal对象。我们只需要实例化对象一次,并且也不需要知道它是被哪个线程实例化。虽然所有的线程都能访问到这个ThreadLocal实例,但是每个线程却只能访问到自己通过调用ThreadLocal的set()方法设置的值。即使是两个不同的线程在同一个ThreadLocal对象上设置了不同的值,他们仍然无法访问到对方的值。

如何访问ThreadLocal变量

一旦创建了一个ThreadLocal变量,你可以通过如下代码设置某个需要保存的值:

my.set("A thread local value”);

可以通过下面方法读取保存在ThreadLocal变量中的值:

String val = (String) my.get();

get()方法返回一个Object对象,set()对象需要传入一个Object类型的参数。

为ThreadLocal指定泛型类型

我们可以创建一个指定泛型类型的ThreadLocal对象,这样我们就不需要每次对使用get()方法返回的值作强制类型转换了。下面展示了指定泛型类型的ThreadLocal例子:

private ThreadLocal my = new ThreadLocal<String>();

现在我们只能往ThreadLocal对象中存入String类型的值了。

并且我们从ThreadLocal中获取值的时候也不需要强制类型转换了。

如何初始化ThreadLocal变量的值

由于在ThreadLocal对象中设置的值只能被设置这个值的线程访问到,线程无法在ThreadLocal对象上使用set()方法保存一个初始值,并且这个初始值能被所有线程访问到。

但是我们可以通过创建一个ThreadLocal的子类并且重写initialValue()方法,来为一个ThreadLocal对象指定一个初始值。这样就可以所有线程共享这个初始化值。代码如下:

ThreadLocal<String> my = new ThreadLocal<String>() {@Overrideprotected String initialValue() {return "init val.";}};

一个完整的ThreadLocal例子

public class Demo03 {public static void main(String[] args) throws InterruptedException {Runnable runnable = new Runnable() {private ThreadLocal<String> threadLocal = new ThreadLocal<String>() {@Overrideprotected String initialValue() {return "init val.";}};@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+threadLocal.get());String val = new Random().nextDouble()+"";System.out.println(Thread.currentThread().getName()+val);threadLocal.set(val);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+threadLocal.get());}};for(int i=1;i<10;i++) {new Thread(runnable).start();}}
}

关于InheritableThreadLocal

InheritableThreadLocal类是ThreadLocal类的子类。ThreadLocal中每个线程拥有它自己的值,与ThreadLocal不同的是,InheritableThreadLocal允许一个线程以及该线程创建的所有子线程都可以访问它保存的值。

【注:所有子线程都会继承父线程保存的ThreadLocal值】

**

二:ThreadLocal的原理

**

ThreadLocal的源码分析

1.每个Thread对象内部维护一个ThreadLocalMap这样的一个<key=ThreadLocal对象,value=要存储的value> Map。

/* ThreadLocal values pertaining to this thread. This map is maintained* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

2.当我们调用ThreadLocal对象的get()方法时,先获取当前线程,然后获取到当前线程的ThreadLocalMap对象,如果非空,则取出ThreadLocal的value,否则进行初始化并返回

 private static ThreadLocal<String> threadLocal = new ThreadLocal<String>();public static void main(String[] args) {threadLocal.get();threadLocal.set("set值");}public T get() {Thread t = Thread.currentThread();//获取当前线程的ThreadLocalMap 引用:return t.threadLocals;ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {//当前ThreadLocalMap不为空,直接取值并返回@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}//ThreadLocalMap为空,则进行初始化,并返回return setInitialValue();}

3.当我们调用set方法时,直接将键值对放入Map即可(Map为空则先创建)

public void set(T value) {Thread t = Thread.currentThread();//获取当前线程的ThreadLocalMap 引用ThreadLocalMap map = getMap(t);//map不为空,直接设值,为空则创建mapif (map != null)map.set(this, value);elsecreateMap(t, value);
}

4.需要注意的是,ThreadLocalMap的Entry,维护了ThreadLocal的弱引用,当调用ThreadLocal的remove()方法时,会清除当前线程的当前ThreadLocal的Entry,垃圾回收就会回收Entry的对应的value对象。

为防止内存泄漏,ThreadLocal需要手动去释放资源

//ThreadLocal.ThreadLocalMap.Entry
static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}public void testThreadLocal() {ThreadLocal<String> threadLocal = new ThreadLocal<String>();//当ThreadLocal不再使用后,为防止内存泄漏,建议手动清空//方式1:清除当前ThreadLocal在ThreadLocalMap中的键值对(Entry)threadLocal.remove();//方式2:将ThreadLocal引用置为nullthreadLocal = null;
}

拓展:

ThreadLocalMap的Hash冲突怎么解决?

Hash冲突怎么解决
和HashMap的最大的不同在于,ThreadLocalMap结构非常简单,没有next引用,也就是说ThreadLocalMap中解决Hash冲突的方式并非链表的方式,而是采用线性探测的方式,所谓线性探测,就是根据初始key的hashcode值确定元素在table数组中的位置,如果发现这个位置上已经有其他key值的元素被占用,则利用固定的算法寻找一定步长的下个位置,依次判断,直至找到能够存放的位置。
ThreadLocalMap解决Hash冲突的方式就是简单的步长加1或减1,寻找下一个相邻的位置。

/*** Increment i modulo len.*/
private static int nextIndex(int i, int len) {return ((i + 1 < len) ? i + 1 : 0);
}/*** Decrement i modulo len.*/
private static int prevIndex(int i, int len) {return ((i - 1 >= 0) ? i - 1 : len - 1);
}

另一个写的较好的博文:
https://blog.csdn.net/qq_23315711/article/details/78642171

Java ThreadLocal相关推荐

  1. Java ThreadLocal 使用详解

    ThreadLocal的官方API解释为: "该类提供了线程局部 (thread-local) 变量.这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每 ...

  2. Java ThreadLocal的使用

    Java中的ThreadLocal类允许我们创建只能被同一个线程读写的变量.因此,如果一段代码含有一个ThreadLocal变量的引用,即使两个线程同时执行这段代码,它们也无法访问到对方的Thread ...

  3. java threadlocal 缺点_Java的ThreadLocal如何在后台实现?

    小编典典 这里的所有答案都是正确的,但是有些令人失望,因为它们多少掩盖了聪明ThreadLocal的实现是多么的明智.我只是在寻找源代码,ThreadLocal并且对它的实现方式印象深刻. 天真的实现 ...

  4. java threadlocal用法_Java ThreadLocal的用法解析

    简介 java中经常使用ThreadLocal作为处理高并发访问的可选手段,ThreadLocal并不是一个线程,而是"以线程为作用域"的一种面向对象的数据结构.其用法的也有着让人 ...

  5. java: ThreadLocal简介

    ThreadLocal中有map会存储每个线程的信息,这样,每个线程相当于拥有了局部变量. package com.baobaotao;class Ticket{public int count = ...

  6. java ThreadLocal理解和使用

    一.ThreadLoal的理解 ThreadLoal 变量,它的基本原理是,同一个 ThreadLocal 所包含的对象(对ThreadLocal< String >而言即为 String ...

  7. java threadlocal 并发_Java并发编程:ThreadLocal

    一.对ThreadLocal的理解 ThreadLocal,很多地方叫做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多.可能很多朋友都知道ThreadLocal为变量在每个线程中都创建了一个 ...

  8. java ThreadLocal用法及原理

    w未完待续 转载于:https://www.cnblogs.com/flying1025/p/6278735.html

  9. Java并发编程之ThreadLocal源码分析

    1 一句话概括ThreadLocal   什么是ThreadLocal?顾名思义:线程本地变量,它为每个使用该对象的线程创建了一个独立的变量副本. 2 ThreadLocal使用场景   用一句话总结 ...

最新文章

  1. Go 语言 bytes.buffer write 相关操作
  2. python 转百分比_2020 | python必读书单
  3. STL中list的使用(理论)
  4. idea springboot 发布webservice 发布服务_阿里云发布 Spring Boot 新脚手架,真香
  5. SQL数据库挂起 SQL数据库附加报错 SQL数据库824错误修复
  6. phpcms上传php,phpcms如何上传视频
  7. php怎么使得字体滚动,滚动文字+字体特效代码(全集)
  8. Linux/Ubuntu: 命令行任务(To-Do List)管理 task - A command line todo manager
  9. ORM框架之Mybatis(六)mybatis通用mapper
  10. Mybatis配置映射文件中parameterType的用法小结
  11. ENVI扩展工具——混合像元分解FCLS
  12. 以swoole为例,学习如何实现协程
  13. C语言Dialogbox添加图片,c# dll c 类_dialogboxparam_msg结构
  14. 融合黄金正弦混合变异的自适应樽海鞘群算法
  15. 高通MTK 安卓手机 手机更改SN 序列号 识别码 教程视频
  16. 北洋降舰之鱼雷艇队 ZT
  17. 【WPS】折线图数据点上添加标记(三角形、正方形、菱形等)
  18. 机器学习入门09 - 特征组合 (Feature Crosses)
  19. 达到英语欧洲语言C2级的书有,剑桥少儿英语二级书
  20. linux 软件覆盖安装,pacman/yaourt安装软件的时候覆盖了/usr/share的symlink链接的问题...

热门文章

  1. MATLAB软件禁用利好,Matlab被禁用?一款国产软件站了出来说:我就是中国的Matlab...
  2. k8s mysql数据同步_K8s——MySQL实现数据持久化
  3. ​如何在图书馆收获一段爱情?​
  4. 中国这10条逆天公路,火爆外网,你都认识多少?
  5. 爱因斯坦留下的预言还有几个未实现?
  6. mysql_contents_将MySQLhelpcontents的内容有层次的输出方法推荐
  7. mysql 导入文件夹_MySQL-导入与导出
  8. curd什么意思中文_每日一句英译英:She's a ten什么意思?
  9. oracle数据库转sqlite,Sqlite导入Oracle软件(SqliteToOracle)
  10. mysql 1117_1117Mysql prepare预处理语句