ThreadLocal
一句话描述
threadLocal,是线程本地变量,每个thread实例持有一个ThreadLocalMap类型的threadLocals属性,map的key为ThreadLocal变量,value为调用threadLocal.set()的值,也就是真正有用的数据。
基础
简单例子
package com.xy.thread.threadlocal.ch01;
/**
* <p>
* 常用写法:
* 1.如果需要设置初始值,可以使用匿名类,或者定义子类继承Threadlocal
* 2.不需要初始值可以直接new ThreadLocal()创建对象
* </p>
*
* @author liufuquan
* @since 2022-02-15
*/
public class NumService implements Runnable{
//匿名类语法,继承类或者实现接口
// initialValue方法为protected方法,继承ThreadLocal类重写initialValue方法设置初始值
private ThreadLocal<Integer> numThreadLocal = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
@Override
public void run() {
for (int i = 0; i < 5; i++) {
Integer integer = numThreadLocal.get();
System.out.println(Thread.currentThread().getName() + ":" + integer);
numThreadLocal.set(++integer);
}
//使用完之后,要清除
numThreadLocal.remove();
}
}
package com.xy.thread.threadlocal.ch01;
import java.util.Scanner;
/**
* <p>
*
* </p>
*
* @author liufuquan
* @since 2022-02-15
*/
public class NumServiceTest {
public static void main(String[] args) {
for (int i = 0; i < 2; i++) {
new Thread(new NumService()).start();
}
Scanner input = new Scanner(System.in);
input.nextLine();
}
}
线程关联的原理
简介
/**
* This class provides thread-local variables.
**/
public class ThreadLocal<T> {}
thread-local variables,官方的注释,说明它的含义,线程本地变量。
ThreadLocal 并不是一个独立的存在, 它与 Thread 类是存在耦合的, java.lang.Thread 类针对 ThreadLocal 提供了如下支持:
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class.
与此线程有关的 ThreadLocal 值。此映射由 ThreadLocal 类维护*/
ThreadLocal.ThreadLocalMap threadLocals = null;
每个线程thread对象中有一个Thread.ThreadLocalMap字段,使用时,调用ThreadLocal对象的set时,放到threadLocalMap中,key为ThreadLocal,value为设置的值。(每个线程都将自己维护一个 ThreadLocal.ThreadLocalMap 类在上下文中; map的key是ThreadLocal对象,value是与线程绑定的,真正用的用到的值。 )
uml关系
代码解析
ThreadLocal.set()方法,set 方法其实是将 target value 放到当前线程的 ThreadLocalMap 中, 而 ThreadLocal 类自己仅仅作为该 target value 所对应的 key
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
get 方法也是类似的道理, 从线程的 ThreadLocalMap 中获取以当前 ThreadLocal 为 key 对应的 value
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
如果没有 set 过 value, 此处 get() 将返回 null或者initialValue()设置的默认值。不过 initialValue() 方法是一个 protected 方法, 需要重写逻辑实现自定义的初始默认值。
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
protected T initialValue() {
return null;
}
使用场景
Spring中@Transactional注解,使用ThreadLocal存储数据库连接connection,保证事务方法每次拿到的都是同一个connection
Spring中Bean在singleton作用域时,使用ThreadLocal解决共享变量的线程安全问题
进阶
ThreadLocalMap数据结构
ThreadLocalMap是哈希表,但是与HashMap不同的是,出现hash冲突后的解决办法:hashmap使用拉链法,ThreadLocalMap使用线性探索法,发生冲突就在数组中向后寻找空的位置插入。
InheritableThreadLocal
一句话描述
InheritableThreadLocal,可继承的线程本地变量,每个线程实例持有ThreadLocalMap类型的inheritableThreadLocals,父线程创建子线程时会将父线程的inheritableThreadLocals复制到子线程的inheritableThreadLocals,从而实现线程本地变量可以传递到子线程。
InheritableThreadLocals类通过重写getMap和createMap两个方法将本地变量保存到了具体线程的inheritableThreadLocals变量中,当线程通过InheritableThreadLocals实例的set或者get方法设置变量的时候,就会创建当前线程的inheritableThreadLocals变量。而父线程创建子线程的时候,ThreadLocalMap中的构造函数会将父线程的inheritableThreadLocals中的变量「复制一份到子线程的inheritableThreadLocals」变量中。
class Thread implements Runnable{
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}
线程的init方法
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
//(1)获取当前线程(父线程)
Thread parent = currentThread();
//(2)父线程的inheritableThreadLocal和inheritThreadLocals=true
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
//(3)父线程的inheritableThreadLocals赋值给子线程的inheritableThreadLocals
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
}
参考资料
https://mp.weixin.qq.com/s/x_dpL66o5S7x_PjncWLvUw
https://mp.weixin.qq.com/s/KbB_3JdR56Mw8O2gJEP2OA