1. 什么是 ThreadLocal
- ThreadLocal 的作用,可以实现在同一个线程数据共享, 从而解决多线程数据安全问题.
- ThreadLocal 可以给当前线程关联一个数据(普通变量、对象、数组)set 方法 [源码!]
- ThreadLocal 可以像 Map 一样存取数据,key 为当前线程, get 方法
- 每一个 ThreadLocal 对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个 ThreadLocal 对象实例
- 每个 ThreadLocal 对象实例定义的时候,一般为 static 类型
- ThreadLocal 中保存数据,在线程销毁后,会自动释放
2. 快速入门 ThreadLocal
● 需求: 演示 ThreadLocal (作用:在一个线程中, 共享数据(线程安全))的使用-画图
创建 Dog.java
public class Dog {
}
创建 Pig.java
public class Pig {
}
创建 T2DAO.java
public class T2DAO {
public void update() {
String name = Thread.currentThread().getName();
System.out.println("在 T2DAO 的 update() 线程是= " +
name + " threadlocal 数据是=" + ThreadLocalTest.threadLocal.get());
System.out.println("在 T2DAO 的 update() 线程是= " +
name + " threadlocal2 数据是=" + ThreadLocalTest.threadLocal2.get());
}
}
创建 T1Service.java
public class T1Service {
public void update(){
//取出threadLocal1对象关联的对象
/**
* 老师解读 get
* public T get() {
* //1. 先得到当前的线程对象
* Thread t = Thread.currentThread();
* //2.通过线程获取到对应的ThrealLocalMap
* ThreadLocalMap map = getMap(t);
* if (map != null) {
* //3. 如果map不为空, 根据当前的 threadlocal对象,得到对应的Entry
* ThreadLocalMap.Entry e = map.getEntry(this);
* //4. 如果e 不为null
* if (e != null) {
* @SuppressWarnings("unchecked")
* //返回当前threadlocal关联的数据value
* T result = (T)e.value;
* return result;
* }
* }
* return setInitialValue();
* }
*
*/
Object o = T1.threadLocal1.get();
//获取当前线程名
String name = Thread.currentThread().getName();
System.out.println("在T1Service的update() 线程name= " +
name + " dog= " + o);
//调用dao-update
new T2DAO().update();
}
}
创 建 ThreadLocalTest.java
public class T1 {
//创建ThreadLocal对象, 做成public static.
public static ThreadLocal<Object> threadLocal1 = new ThreadLocal<>();
public static ThreadLocal<Object> threadLocal2 = new ThreadLocal<>();
//Task 是线程类 -> 内部类 / 线程
public static class Task implements Runnable {
@Override
public void run() {
Dog dog = new Dog();
Pig pig = new Pig();
//给threadLocal1 对象放入set dog , 隔山打牛
System.out.println("Task 放入了 dog= " + dog);
/*
老师解读
public void set(T value) {
//1. 获取当前线程, 关联到当前线程!
Thread t = Thread.currentThread();
//2. 通过线程对象, 获取到ThreadLocalMap
// ThreadLocalMap 类型 ThreadLocal.ThreadLocalMap
ThreadLocalMap map = getMap(t);
//3. 如果map不为null, 将数据(dog,pig..) 放入map -key:threadLocal value:存放的数据
// 从这个源码我们已然看出一个threadlocal只能关联一个数据,如果你set, 就会替换
//4. 如果map为null, 就创建一个和当前线程关联的ThreadLocalMap, 并且该数据放入
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
*/
threadLocal1.set(dog);
//threadLocal1.set(pig);//替换
threadLocal2.set(pig);//这个数据就会threadLocal2关联,并且都被当前Thread管理
System.out.println("Task 在run 方法中 线程=" + Thread.currentThread().getName());
new T1Service().update();
}
}
public static void main(String[] args) {
new Thread(new Task()).start();//主线程启动一个新的线程,注意不是主线程
}
}
完成测试, 检测 ThreadLocal 是否可以实现在同一个线程数据安全共享
3. ThreadLocal 源码解读+画图
3.1 ThreadLocal 原理分析图
- ThreadLocal 原理分析图(重点 set 和 get)
- Debug 源码图,非常重要(老师提醒,最好去 debug 一把,加深印象)
- 老师说明: 这里涉及到的弱引用,涉及到知识点很多,暂不深入.