引导语
ThreadLocal也是线程安全的一种措施,有这么一个经典的比喻“人手一只笔”,如果有100个人要签名,只有一支笔的话,那么肯定要排起长队了,但如果准备了100支笔,那就可以做到每人一支笔啦。
ThreadLocal的官方解释:线程局部变量,是一个以ThreadLocal对象为键,任意对象为值得存储结构,这个结构倍附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询绑定在这个线程上的一个值。(Java并发编程的艺术,P105)
让我们结合源码和示例,来理解ThreadLocal吧!
一、ThreadLocal源码
在Java.lang包下
1、构造方法:只有一个无参构造
public ThreadLocal() {}
2、常用方法:set()
public void set(T value) {
Thread t = Thread.currentThread(); //获得当前线程
ThreadLocalMap map = getMap(t); //获得ThreadLocalMap
if (map != null)
map.set(this, value); //map的常规赋值操作
else
createMap(t, value);
}
- 紧接着,我们看这个getMap()方法做了什么
ThreadLocalMap getMap(Thread t) {
return t.threadLocals; //根据当前线程,从当前线程中取出ThreadLocalMap
}
- 原来存储着各种变量的map在Thread中!让我们来到Thread类的源码中看看:
ThreadLocal.ThreadLocalMap threadLocals = null;
- 回到ThreadLocal源码,再看看createMap()做了什么:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
- 总结:set方法调用时,首先获取当前线程,再从当前线程中获取ThreadLocalMap,如果这个map不为空,则set值,如果这个map是空的,则在当前线程创建ThreadLocalMap,然后再set。
- 注意:ThreadLocalMap是属于线程的,这个map的key是ThreadLocal对象,value就是它在当前线程中的取值。
- 上图:图片来自网络,侵删,谢谢!
3、常用方法:get()
public T get() {
Thread t = Thread.currentThread(); //先得到当前线程
ThreadLocalMap map = getMap(t); //取出当前线程中的ThreadLocalMap
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
三、使用示例
- ThreadLocal一般定义成静态成员变量,以供使用
1、实现了Runnable接口的类
class ThreadLocalTest implements Runnable{
static ThreadLocal<String> str=new ThreadLocal<String>(){
@Override
protected String initialValue() {
return Thread.currentThread().getName(); //设置初始值为线程名
}
};
@Override
public void run(){
try {
//打印出当前线程名,ThreadLocal对象str在当前线程中的值
System.out.println(Thread.currentThread().getName()+":"+str.get());
Thread.sleep(10);
}catch (Exception e){
System.out.println(e);
}
}
}
2、主方法
public class TryThreadLocal {
public static void main(String[] args){
Thread t1=new Thread(new ThreadLocalTest());
Thread t2=new Thread(new ThreadLocalTest());
t1.start();
t2.start();
}
}
3、运行结果
三、应用场景
1、用在数据库连接中,用threadlocal保存Connection对象,可以保证在线程各处的数据库连接都是相同的,而且各个请求之间不影响。
2、用在session管理中,可以保证每个请求的会话不会相互影响。
3、线程上下文相关的信息,例如用户ID、交易ID等等。
感谢:
https://my.oschina.net/davidzhang/blog/111010
https://droidyue.com/blog/2016/03/13/learning-threadlocal-in-java/