正确理解ThreadLocal:ThreadLocal中的值并不一定是完全隔离的
首先再讨论题主的这个观点之前我们要明确一下ThreadLocal的用途是什么?
ThreadLocal并不是用来解决共享对象的多线程访问问题。
看了许多有关ThreadLocal的博客,看完之后会给人一种错觉,ThreadLocal就是用于在多线程情况下防止共享对象的线程安全问题,使用ThreadLocal之后,ThreadLocal的对象就不会有线程安全问题,但是一定是这样么,看如下代码
- public class test {
- public static void main(String[] args) throws InterruptedException {
- new A().start();
- new A().start();
- new A().start();
- new A().start();
- }
- static class A extends Thread {
- static List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
- static ThreadLocal<List<Integer>> threadLocal = new ThreadLocal<List<Integer>>() {
- @Override
- protected List<Integer> initialValue() {
- return list;
- }
- };
- @Override
- public void run() {
- List<Integer> threadList = threadLocal.get();
- threadList.add(threadList.size());
- System.out.println(threadList.toString());
- }
- }
- }
该代码很简单,就是在多线程的情况下输出ThreadLocal的list集合状态,如果此时线程安全的话,输出的4个语句应该是完全一样的输出结果如下:
[1, 2, 3, 4, 5, 5]
[1, 2, 3, 4, 5, 5, 6]
[1, 2, 3, 4, 5, 5, 6, 7]
[1, 2, 3, 4, 5, 5, 6, 7, 8]
很明显可以看到,线程是不安全的,从结果也能看出来4个线程中ThreadLocal中的List是同一个对象,被四个线程所共享。
接下来我们分析一下原因的发生原因,我们去看ThreadLocal的get()方法
- 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();
- }
如果一个线程第一次调用threadLocal.get()方法时,我们通过调试可以发现此时拿到的map是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;
- }
可以看到这个方法里面有一个value,而这个value就是ThreadLocal中即将要保存的只对线程所见的"副本",而这个value使用过initialValue()方法得到的,这个方法如果你没有重写的话默认返回时一个null,而在我们的例子当中他返回的是我们定义的静态变量list,而这个list是一个单例的(只会被类第一次访问时进行初始化),也就是说我们的例子中每个线程的ThreadLocal里面get的都是同一个list,所以就造成了线程安全的问题.
接下来改善一下我们的代码
- static class A extends Thread {
- static ThreadLocal<List<Integer>> threadLocal = new ThreadLocal<List<Integer>>() {
- @Override
- protected List<Integer> initialValue() {
- return new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
- }
- };
- @Override
- public void run() {
- List<Integer> threadList = threadLocal.get();
- threadList.add(threadList.size());
- System.out.println(threadList.toString());
- }
- }
运行结果
[1, 2, 3, 4, 5, 5]
[1, 2, 3, 4, 5, 5]
[1, 2, 3, 4, 5, 5]
[1, 2, 3, 4, 5, 5]
接下来回到主题,ThreadLocal的作用是什么:
ThreadLocal使得各线程能够保持各自独立的一个对象,而实现原理其实是通过,每个线程都会重新创建一个对象,不是什么对象的拷贝或副本,而线程是否安全取决于你如何去创建这个对象。
然后总结一下:
1.ThreadLocal作用不是为了解决共享对象的多线程安全问题,而是为了避免通多参数传递的方式去拿到一个对象,网上有些例子就一开始拿线程安全举例子,然后抛砖引玉出ThreadLocal,会把人带偏。。。博主就是一个。
2.ThreadLocal中保存的不是什么对象的副本,里面没有需要保存的对象做任何复制,拷贝操作,这个对象完全取决于你的iniialtValue方法中如何去创建,所以这里需要考虑使用ThreadLocal的性能问题,是否会大量创建一个对象。
正确理解ThreadLocal:ThreadLocal中的值并不一定是完全隔离的的更多相关文章
- 正确理解Spring AOP中的Around advice
Spring AOP中,有Before advice和After advice,这两个advice从字面上就可以很容易理解,但是Around advice就有点麻烦了. 乍一看好像是Before ad ...
- 正确理解web交互中的cookie与session
cookie存储在客户端的纯文本文件 用户请求服务器脚本 脚本设置cookie内容 并 通过http-response发送cookie内容到客户端并保存在客户端本地 客户端再次发送http请求的时候会 ...
- 正确理解ThreadLocal
想必很多朋友对 ThreadLocal并不陌生,今天我们就来一起探讨下ThreadLocal的使用方法和实现原理.首先,本文先谈一下对ThreadLocal的理 解,然后根据ThreadLocal类的 ...
- Java_正确理解ThreadLocal
首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的.各 ...
- java 多线程 :ThreadLocal 共享变量多线程不同值方案;InheritableThreadLocal变量子线程中自定义值,孙线程可继承
ThreadLocal类的使用 变量值的共享可以使用public static变量的形式,所有的线程都是用同一个public static变量.如果想实现每一个线程都有自己的值.该变量可通过Thr ...
- IM开发基础知识补课(四):正确理解HTTP短连接中的Cookie、Session和Token
本文引用了简书作者“骑小猪看流星”技术文章“Cookie.Session.Token那点事儿”的部分内容,感谢原作者. 1.前言 众所周之,IM是个典型的快速数据流交换系统,当今主流IM系统(尤其移动 ...
- 正确理解DTO、值对象和POCO
今天推荐的文章比较技术化也比较简单,但是对于一些初学者而言,可能也是容易搞混的概念:就是如何理解DTO.值对象和POCO之间的区别. 所谓DTO就是数据传输对象(Data Transfer Objec ...
- C++ : 从栈和堆来理解C#中的值类型和引用类型
C++中并没有值类型和引用类型之说,标准变量或者自定义对象的存取默认是没有区别的.但如果深入地来看,就要了解C++中,管理数据的两大内存区域:栈和堆. 栈(stack)是类似于一个先进后出的抽屉.它的 ...
- clojure中符号symbols 和变量vars的正确理解
原地址 http://stackoverflow.com/questions/11662084/why-does-clojure-distinguish-between-symbols-and-va ...
随机推荐
- Django框架 之 admin管理工具(源码解析)
浏览目录 单例模式 admin执行流程 admin源码解析 单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在 ...
- JavaWeb_增强for循环
引入增强for循环的原因:在JDK5以前的版本中,遍历数组或集合中的元素,需要先获得数组的长度或集合的迭代器,比较麻烦. JDK5中定义了一种新的语法----增强for循环,以简化此类操作.增强for ...
- SSH2+proxool 出现No suitable driver found for proxool.mysqlProxool
SSH2+proxool 出现No suitable driver found for proxool.mysqlProxool 首先我们要明确使用的是SSH2框架,然而Struts2是基于filte ...
- 对 BFC 的理解
对CSS有了解的道友们肯定都知道盒式模型这个概念,对一个元素设置CSS,首先需要知道这个元素是block还是inline类型.而BFC就是用来格式化块级盒子,同样管理inline类型的盒子还有IFC, ...
- win7 64位 [Microsoft][ODBC 驱动程序管理器] 未发现数据源名称并且未指定默认
问题描述: VBA程序连接Access数据库,Excel中执行相关宏,提示[Microsoft][ODBC 驱动程序管理器] 未发现数据源名称并且未指定默认 解决方案: 1.重新安装 AccessDa ...
- linux 进程间通信机制(IPC机制)一总览
1.作用:进程间通信机制(Inter Process Communication,IPC),这些IPC机制的存在使UNIX在进程通信领域手段相当丰富,也使得程序员在开发一个由多个进程协作的任务组成的系 ...
- timestamp 在curl中变成了Xtamp
目前的解决方案 将timestemp放在数组最前面. [注意:请求的参数中需要将timestamp这个参数放在数组的最前面,不然在GET方式请求中,会出现浏览器将它变成Xtamp,最终导致签名失败]
- [SinGuLaRiTy] 2017 百度之星程序设计大赛-资格赛
[SinGuLaRiTy-1034] Copyright (c) SinGuLaRiTy 2017. All Rights Reserved. 度度熊保护村庄 Time Limit: 2000/10 ...
- [SinGuLaRiTy] 2017-07-26 综合性测试
[SinGuLaRiTy-1032] Copyright (c) SinGuLaRiTy 2017. All Rights Reserved. 单词 (word) 题目描述 ...
- linux文件系统相关资料
linux下文件系统通常是通过虚拟文件系统(VFS)蔽下层具体文件系统操作的差异,为上层的操作提供一个统一的接口.文件系统底层都是用系统IO缓存层提供的块读写接口,实现逻辑块到物理块 ...