目录
线程数据共享和安全 -ThreadLocal
什么是 ThreadLocal
代码演示
创建Dog.java
创建Pig.java
T2DAO.java
T2DAO
T1解读set
T1Service 解读 get
ThreadLocalTest这个是换一种法
ThreadLocal 原理分析图
1. ThreadLocal 原理分析图(重点 set 和 get)
线程数据共享和安全 -ThreadLocal
什么是 ThreadLocal
1. ThreadLocal 的作用,可以实现在同一个线程数据共享, 从而解决多线程数据安全问题.
2. ThreadLocal 可以给当前线程关联一个数据(普通变量、对象、数组)set 方法 [源码!]
3. ThreadLocal 可以像 Map 一样存取数据,key 为当前线程, get 方法
4. 每一个 ThreadLocal 对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个 ThreadLocal 对象实例
5. 每个 ThreadLocal 对象实例定义的时候,一般为 static 类型
6. 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);
}
}
T2DAO
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());
}
}
T1解读set
1. 获取当前线程, 关联到当前线程!
2. 通过线程对象, 获取到ThreadLocalMap其中ThreadLocalMap是ThreadLocal的内部类
其中Entry又是ThreadLocalMap的内部类是存放真正的数据的地方 这同样也是为什么是一个线程只能存放一个对象 同样的线程放入会被覆盖
package com.threadlocal;
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();//主线程启动一个新的线程,注意不是主线程
}
}
T1Service 解读 get
1. 先得到当前的线程对象
2.通过线程获取到对应的ThrealLocalMap
3. 如果map不为空, 根据当前的 threadlocal对象,得到对应的Entry
4. 如果e 不为null就继续下面的
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这个是换一种法
public class ThreadLocalTest {
public static ThreadLocal<Object> threadLocal = new ThreadLocal<>();
public static ThreadLocal<Object> threadLocal2 = new ThreadLocal<>();
public static class Task implements Runnable {
@Override
public void run() {
Dog dog = new Dog();
Pig pig = new Pig();
/*
* ======== set 源码分析 只要明白这个机制,后面的 set get 全部通透
public void set(T value) {
获取当前线程
Thread t = Thread.currentThread();
获取当前线程的 ThreadLocal.ThreadLocalMap 属性 threadLocals , 类型是 ThreadLocal 的静态内部类
//threadLocals 有 一 个 属 性 Entry[], 类 型
ThreadLocal.ThreadLocalMap.Entry
//k-> ThreadLocal 对象 v-> 值
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);//存放这里的 this 就是 ThreadLocal, 可以
debug 源码,一目了然
else
createMap(t, value);//创建
}
* ====== getMap 方法源码=====
ThreadLocalMap getMap(Thread t) { // 获取当前线程的
ThreadLocal.ThreadLocalMap
return t.threadLocals;
}
说明:
1. ThreadLocalMap 对象是和当前 Thread 对象的绑定的属性
2. ThreadLocalMap 对象含有 Entry[] table; 这个 Entry(K,V)
3. 这个 key 就是 ThreadLocal 对象, V 就是你要在放入的对象,比如 dog
4. 当执行了 了 threadLocal.set(dog) 后,内存布局图为 wps[看图]
*/
threadLocal.set(dog);
threadLocal.set(pig);//会替换 dog
//如果希望在同一个线程共享多个对象/数据,就在创建一个 ThreadLocal 对象
//threadLocal2.set(pig);
System.out.println("在 run 方法中 线程=" +
Thread.currentThread().getName() + " 放入 threadLocal 的数据=" + dog);
new T1Service().update();
}
}
public static void main(String[] args) {
for (int i = 0; i < 1; i++) {
new Thread(new Task()).start();//启动一个新的线程,注意不是主线程
}
System.out.println("在 main 方法中 threadLocal 的数据=" + threadLocal.get());
}
}
ThreadLocal 原理分析图
1. ThreadLocal 原理分析图(重点 set 和 get)
2. Debug 源码图