首先再讨论题主的这个观点之前我们要明确一下ThreadLocal的用途是什么?

ThreadLocal并不是用来解决共享对象的多线程访问问题。

看了许多有关ThreadLocal的博客,看完之后会给人一种错觉,ThreadLocal就是用于在多线程情况下防止共享对象的线程安全问题,使用ThreadLocal之后,ThreadLocal的对象就不会有线程安全问题,但是一定是这样么,看如下代码

  1.  
    public class test {
  2.  
    public static void main(String[] args) throws InterruptedException {
  3.  
    new A().start();
  4.  
    new A().start();
  5.  
    new A().start();
  6.  
    new A().start();
  7.  
    }
  8.  
     
  9.  
    static class A extends Thread {
  10.  
    static List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
  11.  
    static ThreadLocal<List<Integer>> threadLocal = new ThreadLocal<List<Integer>>() {
  12.  
    @Override
  13.  
    protected List<Integer> initialValue() {
  14.  
    return list;
  15.  
    }
  16.  
    };
  17.  
     
  18.  
    @Override
  19.  
    public void run() {
  20.  
    List<Integer> threadList = threadLocal.get();
  21.  
    threadList.add(threadList.size());
  22.  
    System.out.println(threadList.toString());
  23.  
    }
  24.  
     
  25.  
    }
  26.  
    }

该代码很简单,就是在多线程的情况下输出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()方法

  1.  
    public T get() {
  2.  
    Thread t = Thread.currentThread();
  3.  
    ThreadLocalMap map = getMap(t);
  4.  
    if (map != null) {
  5.  
    ThreadLocalMap.Entry e = map.getEntry(this);
  6.  
    if (e != null) {
  7.  
    @SuppressWarnings("unchecked")
  8.  
    T result = (T)e.value;
  9.  
    return result;
  10.  
    }
  11.  
    }
  12.  
    return setInitialValue();
  13.  
    }

如果一个线程第一次调用threadLocal.get()方法时,我们通过调试可以发现此时拿到的map是null,会调用setInitialValue(),继续看该方法

  1.  
    private T setInitialValue() {
  2.  
    T value = initialValue();
  3.  
    Thread t = Thread.currentThread();
  4.  
    ThreadLocalMap map = getMap(t);
  5.  
    if (map != null)
  6.  
    map.set(this, value);
  7.  
    else
  8.  
    createMap(t, value);
  9.  
    return value;
  10.  
    }

可以看到这个方法里面有一个value,而这个value就是ThreadLocal中即将要保存的只对线程所见的"副本",而这个value使用过initialValue()方法得到的,这个方法如果你没有重写的话默认返回时一个null,而在我们的例子当中他返回的是我们定义的静态变量list,而这个list是一个单例的(只会被类第一次访问时进行初始化),也就是说我们的例子中每个线程的ThreadLocal里面get的都是同一个list,所以就造成了线程安全的问题.

接下来改善一下我们的代码

  1.  
    static class A extends Thread {
  2.  
    static ThreadLocal<List<Integer>> threadLocal = new ThreadLocal<List<Integer>>() {
  3.  
    @Override
  4.  
    protected List<Integer> initialValue() {
  5.  
    return new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
  6.  
    }
  7.  
    };
  8.  
     
  9.  
    @Override
  10.  
    public void run() {
  11.  
    List<Integer> threadList = threadLocal.get();
  12.  
    threadList.add(threadList.size());
  13.  
    System.out.println(threadList.toString());
  14.  
    }
  15.  
     
  16.  
    }

运行结果

[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中的值并不一定是完全隔离的的更多相关文章

  1. 正确理解Spring AOP中的Around advice

    Spring AOP中,有Before advice和After advice,这两个advice从字面上就可以很容易理解,但是Around advice就有点麻烦了. 乍一看好像是Before ad ...

  2. 正确理解web交互中的cookie与session

    cookie存储在客户端的纯文本文件 用户请求服务器脚本 脚本设置cookie内容 并 通过http-response发送cookie内容到客户端并保存在客户端本地 客户端再次发送http请求的时候会 ...

  3. 正确理解ThreadLocal

    想必很多朋友对 ThreadLocal并不陌生,今天我们就来一起探讨下ThreadLocal的使用方法和实现原理.首先,本文先谈一下对ThreadLocal的理 解,然后根据ThreadLocal类的 ...

  4. Java_正确理解ThreadLocal

    首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的.各 ...

  5. java 多线程 :ThreadLocal 共享变量多线程不同值方案;InheritableThreadLocal变量子线程中自定义值,孙线程可继承

      ThreadLocal类的使用 变量值的共享可以使用public static变量的形式,所有的线程都是用同一个public static变量.如果想实现每一个线程都有自己的值.该变量可通过Thr ...

  6. IM开发基础知识补课(四):正确理解HTTP短连接中的Cookie、Session和Token

    本文引用了简书作者“骑小猪看流星”技术文章“Cookie.Session.Token那点事儿”的部分内容,感谢原作者. 1.前言 众所周之,IM是个典型的快速数据流交换系统,当今主流IM系统(尤其移动 ...

  7. 正确理解DTO、值对象和POCO

    今天推荐的文章比较技术化也比较简单,但是对于一些初学者而言,可能也是容易搞混的概念:就是如何理解DTO.值对象和POCO之间的区别. 所谓DTO就是数据传输对象(Data Transfer Objec ...

  8. C++ : 从栈和堆来理解C#中的值类型和引用类型

    C++中并没有值类型和引用类型之说,标准变量或者自定义对象的存取默认是没有区别的.但如果深入地来看,就要了解C++中,管理数据的两大内存区域:栈和堆. 栈(stack)是类似于一个先进后出的抽屉.它的 ...

  9. clojure中符号symbols 和变量vars的正确理解

    原地址  http://stackoverflow.com/questions/11662084/why-does-clojure-distinguish-between-symbols-and-va ...

随机推荐

  1. 树莓派研究笔记(7)-- lakka 《仙剑奇侠传》的完美移植

    仙剑,这是我玩的第一个电脑游戏.真的太经典,无法超越.原来第一次玩的缺少开场动画,很多地方不明不白的.现在终于我们可以把这个梦想继续到树莓派中. LAKKA是支持DOS模拟器的,所以我们一定要下载DO ...

  2. React 和 Redux理解

    学习React有一段时间了,但对于Redux却不是那么理解.网上看了一些文章,现在把对Redux的理解总结如下 从需求出发,看看使用React需要什么 1. React有props和state pro ...

  3. oracle数据库之多表查询二

    多表查询解题思路: 1.分析题目要查询的结果涉及到那几张表 2.将每张表建立关联条件,否则会产生笛卡尔积 3.分清楚查询的条件,然后使用and将条件链接 4.涉及到分组查询的需要使用聚合函数 5.分组 ...

  4. [译]Javascript substring实例

    本文翻译youtube上的up主kudvenkat的javascript tutorial播放单 源地址在此: https://www.youtube.com/watch?v=PMsVM7rjupU& ...

  5. 修改Oracle游标数及排查游标问题相关SQL

    查看当前游标数show parameter open_cursors; 修改游标最大数 ; commit; 查看游标消费情况 select o.sid, osuser, machine, count( ...

  6. Java50道经典习题-程序27 求素数

    题目:求100之内的素数分析:素数即除了1和它本身以外不再有其他因数,最小的素数是2 判断一个数n是否是素数的方法:将n分别与2到(n+1)/2取余,若有一个值为0,则n就不为素数,反之为素数 pub ...

  7. 校对双层PDF中的隐藏文本

    作者:马健邮箱:stronghorse_mj@hotmail.com发布:2012.06.11 目录一.背景二.能够校对的PDF需要满足的条件三.校对工具的选择四.校对过程五.延伸讨论 事先声明:本文 ...

  8. c#桌面应用程序开发--登陆窗口

    一.显示登陆窗口 应用程序入口点为Main方法,因此在Main方法中创建登陆窗体. 1)创建登陆窗体(登陆窗体UI已提前创建好); 2)显示窗体,以模式对话框的形式显示,并赋值给result; 3)判 ...

  9. ASP.NET Core 中的中间件

    前言   由于是第一次写博客,如果您看到此文章,希望大家抱着找错误.批判的心态来看. sky! 何为中间件? 在 ASP.NET Framework 中应该都知道请求管道.可参考:浅谈 ASP.NET ...

  10. form表单操作

    Django之Form组件 Django的Form主要具有一下几大功能: 生成HTML标签 验证用户数据(显示错误信息) HTML Form提交保留上次提交数据 初始化页面显示内容 简单例子  For ...