前言
本篇要说的是ThreadLocal,这个玩意平时在项目中很少用到,但是却有极大的用处;平时在面试中也会经常问到这个问题。
正文
本篇使用jdk1.8版本。
ThreadLocal介绍
先来看看源码中的介绍吧,文档太多,就不全贴出来了
1 |
|
使用方式
在注释中也写明了使用方法:
1 |
|
概括下大概吧:
- ThreadLocal提供一个线程局部变量
- 每个线程只能通过该类的set与get方法获取一个变量
- 独立初始化变量
- 生命周期跟线程的生命周期相同
源码
ThreadLocal中的源码:
首先来看set方法:
1 |
|
接下来在看看get
方法:
1 |
|
再来看看上面两个方法中使用到的方法:
- 第一个,getMap方法:
1 |
|
- createMap方法:
1 |
|
- setInitialValue方法
1 |
|
- initialValue方法:
1 |
|
小结1: 如果在初始化的时候没用重写这个方法,将会返回null,所以这个方法一般会在初始化ThreadLocal的代码中重写这个方法。
在看看Thread中的代码:
1 |
|
小结2:
从上面的代码中可以发现,set
方法通过Thread.currentThread()
来获取当前线程对象,从而操作当前线程对象中的threadLocals
,也就是说通过Threadlocal操作的数据是Thread负责维护的;而threadLocals
又是ThreadLocal
类中的一个静态内部类ThreadLocalMap
,其中key
为this
(这里就是当前的Threadlocal实例对象);
小结3:
- 从源码上看出,不能从解决多线程程序的并发问题和解决多线程访问资源时的共享问题这两个方面去看待Threadlocal的问题。
- 我对
Threadlocal
的大致理解就是:通过Threadlocal
的实例对象a
去操作当前Thread
中ThreadLocal.ThreadLocalMap
对象中key
为a
所对应的数据;
使用场景
1. 数据库连接池
最典型的应该就是数据库连接池了吧,如何处理线程与数据库连接之间的关系,应该就是用到的Threadlocal,具体的就得去谷歌或去看源码了。
2. 信息或参数传递
这里说一下我所用到过的场景:
- 信息传递
有些接口中不会将某些信息作为参数进行传递,但是在这样的情况下却需要某些信息,而这些信息只在最开始的时候能获取到,这时候就可以使用ThreadLocal来操作了。
内存泄漏
关于内存泄漏,其实我在使用过程中也遇到过一次;下面说一下场景吧:
下面是流程图:
我们使用ThreadLocal来对用户信息进行记录,对请求进行拦截,将用户信息存入,但是在有时候会出现游客模式下回显示已登录,还不是自己的账户;
为什么会出现这种情况呢?
- 请求a(带用户信息a)在请求连接池获取线程A
- 拦截器对线程A进行拦截,并对用户信息a保存Threadlocal
- 然后在后台处理中处理A对线程A进行处理,从Threadlocal中获取用户信息a
- 处理完成后,线程A带着用户信息a又回到了请求连接池,Threadlocal的用户信息a也并未清除,且线程A并未被回收
- 请求b(未带用户信息)又在请求连接池获取了线程A,这时未获取用户信息,也未对Threadlocal进行操作
- 回到步骤2
解决办法:
- 拦截器处每次都对Threadlocal进行信息清除(调用Threadlocal的remove方法)
- 即便用户信息为null,也进行保存;
最后
参考文章:
ThreadLocal就是这么简单
Java ThreadLocal
Java进阶(七)正确理解Thread Local的原理与适用场景