近期公司在进行Java开发者的招聘活动,当中有一道面试题是这种:“请简单描写叙述一下ThreadLocal类的作用。” 结果发现有非常多的面试者没有听说过ThreadLocal或者听说过却不知道这个类到底是用来做什么的。

因此这里写一篇博客来介绍一下ThreadLocal这个类。

在我们日常的项目开发中,ThreadLocal并非一个常常使用的类。它很多其它的是被用在诸如Spring,Tomcat或者是Hibernate这些封装了多线程并发的框架或是容器中。

而它的目的也正是为了解决多线程并发訪问共享数据的问题。

 
虽然普通开发者非常少有机会涉及到它。了解ThreadLocal也依旧有助于他们来学习Java并发编程。

通过阅读ThreadLocal的源代码并了解它解决并发问题的思路,开发者能够更好的理解代码中遇到的多线程bug,更不用提那些在项目开发中须要用到多线程编程的开发者了。

因此,不论你是否用到了ThreadLocal类,都非常有必要学习一下它。

 
在我们讨论代码细节之前,先来看看java concurrent in practice中对于多线程并发问题的描写叙述:
     “全部的多线程问题都能够归结为多个线程訪问共享可变状态时的管理问题。

 
这里的状态也就我们说的数据。这句话说明多线程问题必须在下面三个条件都满足的时候才会发生:
1. 拥有多个线程
2. 共享状态
3. 该状态可变
 
假设当中不论什么一个条件没有办法满足。都不会出现多线程问题:
1. 仅仅有单一的线程。 非常显然,这并没有多线程问题。

2. 共享状态不可变。 如果某条数据被多线程共享。然而该数据是不可变数据。那么它便没有多线程问题。

举例来说: Java中的String类型就是不可变的,因此String的共享并不会导致多线程安全问题。

3. 多线程不共享状态。

随意的数据都由某个线程独占,不与其它线程分享,因此也不会出现多线程问题。

 
那么对应的,解决多线程问题的办法有下面几种:
1. 在訪问状态变量时使用同步。

这是最主要的想法,不论什么一本Java多线程编程的书都会具体描写叙述怎样在Java中使用同步,这里不再赘述。

2. 将状态变量改动为不可变的变量。很多新的编程语言,诸如Scala。便是採用这种办法来解决多线程问题的。
3. 避免线程之间共享状态变量。 我们今天讨论的ThreadLocal,便是属于此类解决的方法。
 
刚刚接触ThreadLocal的同学常常会问这样一个问题:“ThreadLocal是线程安全的么?” 这个问题非常难回答,由于当你问这个问题的时候,便默认的觉得ThreadLocal是为了解决多线程之间共享状态的訪问问题的。尽管ThreadLocal的目的正是如此,可是它所採用的办法是“避免多线程之间共享状态”。

既然没有了多线程的共享状态。也就无所谓是否线程安全了。

因此不能简单的说ThreadLocal是否线程安全,这个问题事实上没有意义。

那么ThreadLocal是怎样做到“避免多线程之间的状态共享”的呢?通过在内部维护一个(当前线程 ->对象)的映射表。每一个线程都仅仅能訪问到映射到自己线程的对象。而无法訪问其他线程的对象。通过这样的方法。避免了多线程之间的状态共享,自然也就无所谓线程安全问题了。
 
假设你将某个对象的引用扩散到多个线程中,并将其设置到ThreadLocal里,那么多个线程所指向的便是同一个对象,对它的訪问当然也是有线程安全问题的。从这个角度来讲,ThreadLocal并非线程安全的。

 
换一个角度来描写叙述: TheadLocal并没有真正解决多线程共享状态的安全问题。它仅仅是通过避免状态共享的办法规避了多线程安全问题。

 
我们来看一下ThreadLocal的源代码(JDK1.6):
  1. /**
  2. * Returns the value in the current thread's copy of this
  3. * thread-local variable.  If the variable has no value for the
  4. * current thread, it is first initialized to the value returned
  5. * by an invocation of the {@link #initialValue} method.
  6. *
  7. * @return the current thread's value of this thread-local
  8. */
  9. public T get() {
  10. Thread t = Thread.currentThread();
  11. ThreadLocalMap map = getMap(t);
  12. if (map != null) {
  13. ThreadLocalMap.Entry e = map.getEntry(this);
  14. if (e != null)
  15. return (T)e.value;
  16. }
  17. return setInitialValue();
  18. }
能够看到,当ThreadLocal的get方法被调用时,首先利用当前线程作为key获得了一个map,而这个map便是当前线程专属的,其他线程无法訪问。在从该Map中找到对应的对象并返回。
 
而set方法正好相反:
  1. /**
  2. * Sets the current thread's copy of this thread-local variable
  3. * to the specified value.  Most subclasses will have no need to
  4. * override this method, relying solely on the {@link #initialValue}
  5. * method to set the values of thread-locals.
  6. *
  7. * @param value the value to be stored in the current thread's copy of
  8. *        this thread-local.
  9. */
  10. public void set(T value) {
  11. Thread t = Thread.currentThread();
  12. ThreadLocalMap map = getMap(t);
  13. if (map != null)
  14. map.set(this, value);
  15. else
  16. createMap(t, value);
  17. }

我们来看一个在Hibernate中使用ThreadLocal的样例:

  1. private static ThreadLocal<Connection> connectionHolder
  2. = new ThreadLoca<Connection>() {
  3. public Connection initialValue() {
  4. return DriverManager.getConnection(DB_URL);
  5. }
  6. };
  7. public static Connection getConnection() {
  8. return ConnectionHolder.get();
  9. }

上面的样例是一个最经典的ThreadLocal使用案例: 在单线程中创建一个单例变量。并在程序启动时初始化该单例变量,从而避免在调用每个方法时都要传递该变量。然而该单例可能并非线程安全的,因此,当多线程应用程序在没有互相协作的情况下,能够通过将该单例变量保存到ThreadLocal中。以确保每个线程都拥有属于自己的实例。

在这里,一个更好理解的说法便是:该单例是一个线程内单例,在多线程应用中,每一个线程里都有一个该单例。

通过学习ThreadLocal,我们可以对正确的在项目中使用它。同一时候。也可以帮助我们对多线程编程有一个更深的认识.

(该文同一时候发表在http://mabusyao.iteye.com/blog/2224898)

初识ThreadLocal的更多相关文章

  1. 初识Java ThreadLocal

    转载自:https://www.cnblogs.com/dreamroute/p/5034726.html ThreadLocal翻译成中文比较准确的叫法应该是:线程局部变量. 这个玩意有什么用处,或 ...

  2. ThreadLocal详解,ThreadLocal源码分析,ThreadLocal图解

    本文脉路: 概念阐释 ---->  原理图解  ------> 源码分析 ------>  思路整理  ----> 其他补充. 一.概念阐述. ThreadLocal 是一个为 ...

  3. Java并发编程:ThreadLocal的使用以及实现原理解析

    前言 前面的文章里,我们学习了有关锁的使用,锁的机制是保证同一时刻只能有一个线程访问临界区的资源,也就是通过控制资源的手段来保证线程安全,这固然是一种有效的手段,但程序的运行效率也因此大大降低.那么, ...

  4. ThreadLocal简单理解

    在java开源项目的代码中看到一个类里ThreadLocal的属性: private static ThreadLocal<Boolean> clientMode = new Thread ...

  5. Android线程管理之ThreadLocal理解及应用场景

    前言: 最近在学习总结Android的动画效果,当学到Android属性动画的时候大致看了下源代码,里面的AnimationHandler存取使用了ThreadLocal,激起了我很大的好奇心以及兴趣 ...

  6. Threadlocal使用Case

    Threadlocal能够为每个线程分配一份单独的副本,使的线程与线程之间能够独立的访问各自副本.Threadlocal 内部维护一个Map,key为线程的名字,value为对应操作的副本. /** ...

  7. Android动画效果之初识Property Animation(属性动画)

    前言: 前面两篇介绍了Android的Tween Animation(补间动画) Android动画效果之Tween Animation(补间动画).Frame Animation(逐帧动画)Andr ...

  8. 多线程映射工具——ThreadLocal

    ThreadLocal相当于一个Map<Thread, T>,各线程使用自己的线程对象Thread.currentThread()作为键存取数据,但ThreadLocal实际上是一个包装了 ...

  9. ThreadLocal 工作原理、部分源码分析

    1.大概去哪里看 ThreadLocal 其根本实现方法,是在Thread里面,有一个ThreadLocal.ThreadLocalMap属性 ThreadLocal.ThreadLocalMap t ...

随机推荐

  1. 暑假集训-WHUST 2015 Summer Contest #0.1

    ID Origin Title   4 / 12 Problem A Gym 100589A Queries on the Tree 14 / 41 Problem B Gym 100589B Cou ...

  2. Mysql学习总结(3)——MySql语句大全:创建、授权、查询、修改等

    一.用户创建.权限.删除 1.连接MySql操作 连接:mysql -h 主机地址 -u 用户名 -p 用户密码 (注:u与root可以不用加空格,其它也一样) 断开:exit (回车) 打开cmd, ...

  3. You have ettempted to queue to many files.You may select one files.

    <script type="text/javascript" src="/script/swfupload/swfupload.js"></s ...

  4. mysql通过字段凝视查找字段名称

    有时候表的字段太多.仅仅是大致记得表的凝视,想通过字段凝视查找字段名称,能够用例如以下语句: SELECT COLUMN_NAME,column_comment FROM INFORMATION_SC ...

  5. PhoneGap/Cordova Android应用签名公布注意事项

    今天最终要公布Android HybirdApp了,安装曾经做原生应用的流程公布签名Apk,没想到立即遇到了几个问题.如今把它们的解决的方法整理下来. export signed Apk 遇到以下错误 ...

  6. android 图片特效处理之 图片叠加

    这篇将讲到图片特效处理的图片叠加效果.跟前面一样是对像素点进行处理,可参照前面的android图像处理系列之七--图片涂鸦,水印-图片叠加和android图像处理系列之六--给图片添加边框(下)-图片 ...

  7. Toeplitz matrix 与 Circulant matrix

    之所以专门定义两个新的概念,在于它们特殊的形式,带来的特别的形式. 1. Toeplitz matrix 对角为常数: n×n 的矩阵 A 是 Toepliz 矩阵当且仅当,对于 Ai,j 有: Ai ...

  8. 值得学习的CSS知识

    这里零度给大家推荐几个值得学习的CSS技巧,能让你编写网页事半功倍!一.清除默认值 通常 padding 的默认值为 0,background-color 的默认值是 transparent.但是在不 ...

  9. Java: 数据类型

    核心:对事物的某种规范   前提: 1.JAVA:JAVA程序的运行是以堆栈的操作来完成的  堆栈以帧为单位保存线程的状态.       JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作. 理解 ...

  10. vmstat---有关进程、虚存、页面交换空间及 CPU信息

    虚拟内存运行原理 在系统中运行的每个进程都需要使用到内存,但不是每个进程都需要每时每刻使用系统分配的内存空间.当系统运行所需内存超过实际的物理内存,内核会释放某些进程所占用但未使用的部分或所有物理内存 ...