(Java多线程常见面试题)ThreadLocal 是什么?有哪些使⽤场景?

最近在研究多线程项目时,无意间看到一个很有意思的Java类----ThreadLocal。于是乎一向对于新东西充满好奇的我又开始了一系列深挖细究,在经过学习和参考网上其他大佬的见解后,现将自己的理解作一记录与总结。

一、ThreadLocal是什么?它是干嘛的?
  ThreadLocal是java早在JDK 1.2的版本中就提供的java.lang.ThreadLocal类,用于线程间的数据隔离,从而实现线程安全的一种机制。

      我们都知道多线程编程中逃不掉的一个技术难点就是解决线程安全问题。常见或常用的处理线程安全的手段就是使用锁机制 来保证多个线程对同一个变量(共享变量)同一时刻只能有一个线程访问,来解决多线程线程间的数据共享带来的并发问题。

     相较于通过锁机制,如Synchronized自动锁 来解决多线程数据共享带来的并发问题(本质是时间换空间的做法),ThreadLocal为解决多线程程序的并发问题提供了另一种新的思路(空间换时间的做法)。那么ThreadLocal究竟是怎么实现的呢?又为什们说是空间换时间呢(咋理解这句话呢)?请接着往下看。

 二、 原理

       ThreadLocal会为每一个线程提供一个独立的变量副本(或叫线程局部变量)[即ThreadLocalMap副本,后文会详细介绍],每一个线程都可以独立地改变自己的线程局部变量,而不会影响其它线程所对应的线程局部变量,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量,从而也就没有必要对该变量进行同步了。

       ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。【补充: Spring使用ThreadLocal解决线程安全问题。我们知道在一般情况下,只有无状态的Bean(就是没有实例变量的对象,不能保存数据,是不变类,是线程安全的)才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行了处理,让它们也成为线程安全的状态,所以有状态的Bean就可以在多线程中共享了】

  概括总结一下就是,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程互斥访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响
 

 三、 原理背后的底层实现揭秘

      说了半天TheadLocal是用来实现线程数据隔离的,那么,数据隔离底层是怎么实现的嘞?   线程局部变量的值又存储到TheadLocal中的什么地方捏?

      在介绍ThreadLocal之前先来提一下TheadLocalMap。 在Thread线程类中有个TheadLocalMap类型的属性,叫做threadLocals,该属性就是用来保存该线程的本地变量。这样每个线程都有自己的数据,就做到了不同线程间数据的隔离,保证了数据安全。那TheadLocalMap又是谁? 哪来的? 数据又是怎么保存到它里面去的呢?  让我们先来扒拉一下这个东东,看看藏着什么秘密。

     ThreadLocalMap  看名字就知道是个map,没错,它就是个hashMap机制实现的map,用来存储数据,存储了以threadLocal对象为key,需要隔离的数据为value的Entry键值对数组结构。

    好了,现在可以正式开始叨叨主角ThreadLocal。

  ThreadLocal  中定义了个ThreadLocalMap静态内部类,結合上面介绍可知存储的数据就放在ThreadLocal  中的ThreadLocalMap中。这样,就实现了ThreadLocal连接起ThreadLocalMap和Thread,用来处理Thread的TheadLocalMap属性(具体通过ThreadLocal提供的方法包括init初始化属性赋值、get对应的变量,set设置变量等)。通过当前线程,获取线程上的ThreadLocalMap属性,对数据进行get、set等操作。[后面会结合源码分析详细介绍]

接下来用jdk1.8源码进行深挖一下Thread、TheadLocal与TheadLocalMap三者之间的关系。

Thread的属性:

(Java多线程常见面试题)ThreadLocal 是什么?有哪些使⽤场景?_第1张图片


ThreadLocal和ThreadLocalMap

(Java多线程常见面试题)ThreadLocal 是什么?有哪些使⽤场景?_第2张图片


由ThreadLocal对Thread的TreadLocalMap进行赋值(相当于代理)

(Java多线程常见面试题)ThreadLocal 是什么?有哪些使⽤场景?_第3张图片

小结:  

通过前面介绍再结合上述部分源码我们就可以知道: 每个线程中独立的ThreadLocalMap副本所存储的值,只能被当前线程读取和修改。ThreadLocal类通过操作每一个线程特有ThreadLocalMap副本,从而实现了变量访问在不同线程中的隔离。因为每个线程的变量都是自己特有的,完全不会有并发错误。(  所以ThreadLocal可以简单看成一个map,每个线程保存一个map,这个map只能存储一组数据,key为线程id, value就是要存的值。)

另外值得注意的是:一个ThreadLocal只能存储一个变量,如果要存多个就创建多个ThreadLocal对象。

四、 ThreadLocal的应用场合

        ThreadLocal的应用场合,最适合的是按线程多实例(每个线程对应一个实例)的对象的访问,并且这个对象很多地方都要用到。应用场景举例:总之,解决线程安全问题,对于需要进行线程隔离的变量,可以使用ThreadLocal存储,确保线程隔离

  •  多线程环境下为每一个jdbc分配一个Connection连接,使用ThreadLocal去保存连接,这样就保证了每个线程在自己的连接上操作数据库,不会出现A线程关闭B的Connnection操作。
  • Web中的session管理时,可以使用ThreadLocal记录每个线程的Session,这样保证每个线程都可以获取自己的session     

    总之,解决线程安全问题,对于需要进行线程隔离的变量,可以使用ThreadLocal存储,确保线程隔离。 

五、 ThreadLocal总结(结合锁机制Synchronized)

  •  Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
  • 在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。而ThreadLocal则从另一个角度来解决多线程的并发访问。
  • ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。[底层原理是:ThreadLocal在每个本地线程中创建了一个ThreadLocalMap对象,每个线程可以访问自己内部ThreadLocalMap对象里的value。通过这种方式,实现线程之间的数据隔离。]

                                                                          

你可能感兴趣的:(Java面经及八股,java,面试)