正确理解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 ...
随机推荐
- 前端学习笔记2017.6.21-html是个什么东西
html有两种意思,html语言和html格式 html语言是一种面向人类的计算机语言,这是啥意思?人类用html这种语言描述出一个网页的样子,浏览器解析这个语言并展示出来. html格式是一种文件格 ...
- java中的全局变量与静态变量的区别与联系?有时候为什么专门定义静态变量。(标题党~~)
static代表“每个类一个”而不是“每个对象一个”.即静态变量是类的所有对象共有的. 1 static JFrame f; static MyDrawPanel ml; 整个应用程序如下: 定义在p ...
- 1用java实现冒泡排序算法以及解决的几个小问题。
package huawei.exam; public class BubbleSort { /** * @param args */ public static void main(String[] ...
- 一劳永逸搭建android开发环境(android官网reference sample api tutorial全下载)
[摘要]本文简单介绍了android开发环境的搭建,重点介绍了SDK manager和AVD升级问题:并提供了android reference,sample,api,及docs的下载信息. [1]为 ...
- 国外物联网平台(2):微软Azure IoT
国外物联网平台(2)——微软Azure IoT 马智 平台定位 连接设备.其它 M2M 资产和人员,以便在业务和操作中更好地利用数据. 连接 IoT 设备 将所有设备连接到云,从这些设备接收大规模数据 ...
- webapi 返回json
web api 默认的已 xml 格式返回数据 现在开发一般都是以 json 格式为主 下面配置让 webapi 默认返回 json ,在需要返回 xml 时只需要加一个查询参数 datatype=x ...
- cin、cout、cerr、clog------c++ Primer Plus
cin对象与标准输入流相对应. cout对象与标准输出流相对应. cerr对象与标准错误流相对应,常用于程序错误信息,不缓冲,直接被发送给屏幕. clog对象也对应标准错误流(这点儿和cerr是一样的 ...
- python之编辑器pycharm
在进行python开发的时候,习惯使用pycharm这个编辑器进行开发工作,总结一些常用到的功能点 常用功能: 1. 打开当前文件所在的目录 在文件右击 -> Show in Exploer ...
- ubantu一些资料
1.在引导页面中选中win10 按E键 进入编辑 在最后一行后面加上一行ntldr /bootmgr 再按F10引导,发现可以进入windows10 2.ubantu安装qq https://lear ...
- 第一次接触C++------感触
2018/09/24 上大学第一次接触C++,感觉还挺有趣的. C语言是计算机的一门语言,顾名思义,语言嘛,有它自己独特的语法. 第一次用C++敲代码,觉得还挺不错的,可以从中找到乐趣.咏梅老师布置的 ...