【Java并发解析】ThreadLocal解析

1. ThreadLocal的使用场景

  1. 工具类不是线程安全的,我们让每个线程都有一个独立的工具类。
  2. 避免参数传递的麻烦。
    【Java并发解析】ThreadLocal解析_第1张图片

场景1.每个线程需要一个独享的对象,常用在工具类中

  • 当多个线程多次使用同一个线程不安全的对象时,会发生线程安全问题。可以使用线程池+synchronized的方法,但这种方法的性能差。
  • 采用ThreadLocal方法,对10个线程都创建一个dateFormat对象。每一个dateFormat的是线程安全的。实际上是可以并行的。在ThreadLocal中需要重写initialValue()。生成一个新的结构。

场景2. 每个线程内保存一个内部变量,不同的方法可以直接使用,常用于对一个对象进入多个方法。

比如多个用户请求,每个线程都对应了不同的用户信息,我们希望在一个线程内维护一个变量,在调用方法的时候是通用的。可能的做法包括采用ConucurrentHashMap等。但是这种方法会导致性能降低。

  • 采用ThreadLocal可以不影响性能,且无需层层传递参数。在线程周期内,通过静态的ThreadLocal实例的get()方法取得自己Set的对象,避免了传参的麻烦。
  • 不需要重写initialValue()方法,但是必须手动调用set(),在同一个线程的其他程序调用时,直接get()

2. ThreadLocal的优点

  1. 可以实现线程安全
  2. 不需要加锁,提高了实行效率
  3. 更高效的利用内存,节省开销。
  4. 避免的传参的繁琐。

3. 源码分析

简单来说就是每一个Thread都对应了一个ThreadLocalMap,而每一个ThreadLocalMap里面存在多个ThreadLocal。这样是为了在一个线程内可以读取多个变量。

【Java并发解析】ThreadLocal解析_第2张图片

重要方法解析

  1. T initialValue(): 返回线程对应的初始值,延迟加载,只有在调用get时候才会触发。如果之前set了值,就不需要了。每个线程左右调用一次就可以。如果不重写,就会返回null。
  2. void set(T t): 为这个线程设置一个新值
  3. T get(): 得到这个线程对应的value。
  4. void remove(): 删除线程的值。

ThreadLocalMap类

也就是Thread.threadLocals。核心是一个键值对数组Entry[] table

  • 处理哈希冲突:冲突是开放寻址法。
  • 传统哈希:初始时拉链法,后续变为红黑树。

4.ThreadLocal注意点

内存泄露:某个对象不在使用,但占用的内存无法被回收。

  1. key 是一个弱引用,是可以被垃圾回收的。
  2. value是一个强引用。如果线程结束,线程是被回收的。但是如果使用线程池,线程保持,因此value无法被回收,引发内存泄漏。JDK已经考虑了,在调用setremoverehash方法时候会把key是null的的value也设置为null。但是一旦ThreadLocal不用了,一般也很难去调用remove。容易造成泄露。
  3. 避免方法:使用完ThreadLocal,主动调用remove。也就是说,在最后一个方法调用完ThreadLocal,需要主动remove

共享对象

  1. 注意不要存入一个static对象,否则还是会导致线程安全的问题。

不要强行使用

  1. 比较少的线程没必要用。

你可能感兴趣的:(Java并发解析)