ThreadLocal白话讲解

138 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

threadlocal是什么

每个线程所拥有的私有存储空间,线程间不可访问对方的存储空间。

threadlocal怎么用

是一个以 ThreadLocal 对象为键、任意对象为值的存储结构

ThreadLocal 类常用方法,如下:

protected T initialValue():设置初始值,默认为null
public void set(T value):设置一个要保存的值,并会覆盖原来的值
public T get():获得保存的值,如果没有用过set方法,会获取到初始值
public void remove():移除保存的值

threadlocal需要注意

  1. 在线程池中使用

线程池中的线程是可以复用的。每个request请求可复用线程,在复用情况下,若往线程中存入值,复用后的线程中也能拿到值。

存在的问题:

你在某个线程中 threadlocal存入了值any,则通过这个any判断不走某逻辑A。多次request请求,这多次都未存入any值,可还是没走某逻辑A。问题出在多次request请求时,其他有复用了之前的线程,之前线程存入了any值。

解决方案:

每次存入值any后,并进行不走逻辑A后,进行清理any值。通过调用remove()方法。

  1. 内存泄露问题

request和ThreadLocal的差别

1)存取值方式不同。

 request根据KEY存取值、一个request可以存多个值。

 ThreadLocal只能存一个值,ThreadLocal的getset方法没有参数KEY

2)使用地方不一样。

 request使用在表示层、一般在Action和Servlet中使用。

 ThreadLocal在什么地方都可以、一般用在框架基类中比较多、比如存放当前的数据库连接等。

ThreadLocal的设计是‘空间换时间’,synchronized顺序执行是‘时间换取空间’(抛出问题:synchronized与CAS)

1.为什么ThreadLocalMap使用弱引用存储ThreadLocal?

假如使用强引用,当ThreadLocal不再使用需要回收时,发现某个线程中ThreadLocalMap存在该ThreadLocal的强引用,无法回收,造成内存泄漏。

因此,使用弱引用可以防止长期存在的线程(通常使用了线程池)导致ThreadLocal无法回收造成内存泄漏。

2.那通常说的ThreadLocal内存泄漏是如何引起的呢?

我们注意到Entry对象中,虽然Key(ThreadLocal)是通过弱引用引入的,但是value即变量值本身是通过强引用引入。

这就导致,假如不作任何处理,由于ThreadLocalMap和线程的生命周期是一致的,当线程资源长期不释放,即使ThreadLocal本身由于弱引用机制已经回收掉了,但value还是驻留在线程的ThreadLocalMap的Entry中。即存在key为null,但value却有值的无效Entry。导致内存泄漏。

但实际上,ThreadLocal内部已经为我们做了一定的防止内存泄漏的工作。

抛出问题:

  1. threadlocal为什么要定义成static 、final?

定义成static即为静态变量,通过类对象进行访问。定义为final是不能修改,旨在当前线程中进行set、get值,所以不需要进行修改,而且也不能修改,所以定义为final。

  1. threadlocal为什么设置key为弱引用?

防止内存溢出。threadlocal中存入的map与当前线程的生命周期一致,弱引用是让key的生命周期不跟着当前线程,减小内存泄露的风险。

  1. 如何避免threadlocal内存溢出? 及时调用remove方法