概述
ThreadLocal直译为线程局部变量,或许将它命名为ThreadLocalVariable更为合适。其主要作用就是实现线程本地存储功能,通过线程本地资源隔离,解决多线程并发场景下线程安全问题。
API
![](https://i-blog.csdnimg.cn/blog_migrate/de63ec7b0cdb31a00650fe6f4f86f29c.png)
实例
package com.starsray.test.tl;
import java.util.ArrayList;
import java.util.List;
public class ThreadLocalTest {
// 声明一个ThreadLocal成员变量
private final ThreadLocal<Person> tl = new ThreadLocal<>();
// 声明一个List作为参照对象
private final List<Person> list = new ArrayList<>();
public static void main(String[] args) {
new ThreadLocalTest().test();
}
public void test() {
// 创建测试Person对象
Person person = new Person();
person.setName("张三");
person.setAge(24);
// 创建线程一:再启动1s后,分别添加person对象到tl、list对象中
new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
tl.set(person);
list.add(person);
System.out.println(Thread.currentThread().getName() + " [thread] get():" + tl.get());
System.out.println(Thread.currentThread().getName() + " [list] get():" + list.get(0));
},"thread-1").start();
// 创建线程二:在启动2s后,分别去tl、list对象中取person对象
new Thread(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " [thread] get():" + tl.get());
System.out.println(Thread.currentThread().getName() + " [list] get():" + list.get(0));
},"thread-2").start();
}
// 测试静态内部类Person
static class Person {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
}
结果:
hread-1 [thread] get():Person{name='张三', age=24}
thread-1 [list] get():Person{name='张三', age=24}
thread-2 [thread] get():null
thread-2 [list] get():Person{name='张三', age=24}
分析:
看结果可以知道,线程之间的数据是隔离的,每个Thread维护了一份属于自己的数据。
原理分析
每个Thread对象都有一个ThreadLocalMap,当创建一个ThreadLocal时,就会将该ThreadLocal的ThreadLocalHashCode值(唯一)作为Key,数据作为Value存储在ThreadLocalMap中。也就是说数据其实都在Thread中。
向ThreadLocal存入一个值,实际上是向当前线程对象中的ThreadLocalMap存入值,ThreadLocalMap我们可以简单的理解成一个Map,而向这个Map存值的key就是ThreadLocal实例本身。
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);//获取当前Thread的ThreadLocalMap
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//this就是当前ThreadLocal实例
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;//threadLocals就是Thread中ThreadLocalMap的变量名
}
也就是说,想要存入的ThreadLocal中的数据实际上并没有存到ThreadLocal对象中去,而是以这个ThreadLocal实例作为key存到了当前线程中的一个Map中去了,获取ThreadLocal的值时同样也是这个道理。这也就是为什么ThreadLocal可以实现线程之间隔离的原因了。
应用场景
针对ThreadLocal而言,由于其适合隔离、线程本地存储等特性,因此天然的适合一些Web应用场景,比如下面所列举的例子:
代替参数显式传递(很少使用,暂时不理解)
存储全局用户登录信息
存储数据库连接,以及Session等信息
Spring事务处理方案(暂时不理解)
总结
ThreadLocal的作用:实现线程范围内的局部变量,即ThreadLocal在一个线程中是共享的,在不同线程之间是隔离的。
ThreadLocal的原理:ThreadLocal存入值时使用当前ThreadLocal实例作为key,存入当前线程对象中的Map中去。
参考链接
https://blog.csdn.net/qq_38721537/article/details/124565091
https://blog.csdn.net/tianjindong0804/article/details/85597215