1、ThreadLocal介绍
ThreadLocal是java为了解决线程使用共享变量相互隔离,相互不干扰而提供的类
话不多说直接上使用方法
public class Test {
// 初始化ThreadLocal并且设置初始值为0
private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);
public static void main(String[] args) {
for(int i=0;i<5;i++) {
Thread t = new Thread(()->{
Integer value = threadLocal.get();
value = value + 5;
// 每个线程都对threadLocal 的值加5
threadLocal.set(value);
System.out.println("线程结果:"+threadLocal.get());
});
t.start();
}
}
}
调用结果如下:
线程结果:5
线程结果:5
线程结果:5
线程结果:5
线程结果:5
上述代码介绍了不同线程使用同一个ThreadLocal对象,但是各自操作时都是互不干扰的场景
2、ThreadLocal原理
我们直接看下源码来理解下原理,源码来自jdk8
public void set(T var1) {
// 获取当前线程
Thread var2 = Thread.currentThread();
// 从当前线程获取ThreadLocalMap,ThreadLocalMap在每个Thread都是一个成员变量
ThreadLocal.ThreadLocalMap var3 = this.getMap(var2);
if (var3 != null) {
var3.set(this, var1);
} else {
// 当前线程ThreadLocalMap为空则初始化
this.createMap(var2, var1);
}
}
public T get() {
Thread var1 = Thread.currentThread();
/**
* 从当前线程获取ThreadLocalMap,由于每个Thread都有属于自己的ThreadLocalMap,
* 这就是ThreadLocal解决线程使用共享变量相互隔离的关键之处
*/
ThreadLocal.ThreadLocalMap var2 = this.getMap(var1);
if (var2 != null) {
// 根据当前对象threadLocal对象为key在Entry[]里面查找对应的Entry
ThreadLocal.ThreadLocalMap.Entry var3 = var2.getEntry(this);
if (var3 != null) {
// Entry对象返回值
Object var4 = var3.value;
return var4;
}
}
// 如果没有set过则返回默认值
return this.setInitialValue();
}
ThreadLocalMap的set方法,即设置的key为当前threadLocal,value即为需要设置的值
private void set(ThreadLocal<?> var1, Object var2) {
// ThreadLocalMap存储数据的方式是Entry数组,Entry里面保存存储的value
ThreadLocal.ThreadLocalMap.Entry[] var3 = this.table;
int var4 = var3.length;
int var5 = var1.threadLocalHashCode & var4 - 1;
for(ThreadLocal.ThreadLocalMap.Entry var6 = var3[var5]; var6 != null; var6 = var3[var5 = nextIndex(var5, var4)]) {
ThreadLocal var7 = (ThreadLocal)var6.get();
if (var7 == var1) {
var6.value = var2;
return;
}
if (var7 == null) {
this.replaceStaleEntry(var1, var2, var5);
return;
}
}
var3[var5] = new ThreadLocal.ThreadLocalMap.Entry(var1, var2);
int var8 = ++this.size;
if (!this.cleanSomeSlots(var5, var8) && var8 >= this.threshold) {
this.rehash();
}
}
Entry对象,还是一个弱引用,这里如果把threadLocal设置为null,则Entry即为无效对象,属于内存泄漏,ThreadLocal在调用remove、get、set等方法时会检测并且清楚内存泄漏的无效对象
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> var1, Object var2) {
super(var1);
this.value = var2;
}
}
上述代码相对容易理解,ThreadLocal的实现方式主要是每个Thread包含一个ThreadLocalMap,在每个线程中ThreadLocalMap都是独立的,所以可以达到线程使用共享变量之间的相互隔离
3、使用场景
ThreadLocal的使用场景主要是业务需求为:在不同线程都希望可以获取到同一个对象的情况,如在方法A设置了某个对象到ThreadLocal,希望在同一个线程调用方法B、C都可以拿到共享参数
注:使用时尽量在使用完后调用remove(),原因是如果在线程池中核心线程不会被回收,所以该核心线程会一直存在ThreadLocal的值,并且在其他功能使用了同一个线程时会导致没有设置值但是可以取到值的情况