1、什么是ThreadLocal
官方介绍:该类提供了线程局部(thread-local)变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。
ThreadLocal是为每个线程创建一个单独的变量副本,每个线程都可以改变自己的变量副本而不影响其它线程所对应的副本。
2、ThreadLocal的API
ThreadLocal定义了四个方法:
- get():返回此线程局部变量当前副本中的值
- set(T value):将线程局部变量当前副本中的值设置为指定值
- initialValue():返回此线程局部变量当前副本中的初始值
- remove():移除此线程局部变量当前副本中的值
ThreadLocal还有一个特别重要的静态内部类ThreadLocalMap,该类才是实现线程隔离机制的关键。get()、set()、remove()都是基于该内部类进行操作,ThreadLocalMap用键值对方式存储每个线程变量的副本,key为当前的ThreadLocal对象,value为对应线程的变量副本。每个线程都有自己的ThreadLocal对象,也就是都有自己的ThreadLocalMap,对自己的ThreadLocalMap操作,当然是互不影响的了,这就不存在线程安全问题了,所以ThreadLocal是以空间来交换安全性的解决思路。
3、使用场景
场景一:通过AOP的方式配置动态数据源
我们使用注解,标记该方法或者该类将要使用的数据源,在切面处理类对数据源进行设置,此时不同的线程之间应该要不互相影响,此时多数据源上下文存储当前线程所使用的数据源应该是线程局部变量,这样不同线程之间就不会照成影响。
package com.hsshy.beam.dynamic.datasource.config;
import java.util.ArrayDeque;
import java.util.Deque;
/**
* 多数据源上下文
*/
public class DynamicContextHolder {
@SuppressWarnings("unchecked")
private static final ThreadLocal<Deque<String>> CONTEXT_HOLDER = new ThreadLocal() {
@Override
protected Object initialValue() {
return new ArrayDeque();
}
};
/**
* 获得当前线程数据源
*
* @return 数据源名称
*/
public static String peek() {
return CONTEXT_HOLDER.get().peek();
}
/**
* 设置当前线程数据源
*
* @param dataSource 数据源名称
*/
public static void push(String dataSource) {
CONTEXT_HOLDER.get().push(dataSource);
}
/**
* 清空当前线程数据源
*/
public static void poll() {
Deque<String> deque = CONTEXT_HOLDER.get();
deque.poll();
if (deque.isEmpty()) {
CONTEXT_HOLDER.remove();
}
}
}
场景二:假设每个线程都需要一个计数值记录自己做某件事做了多少次,各线程运行时都需要改变自己的计数值而且相互不影响,那么ThreadLocal就是很好的选择,这里ThreadLocal里保存的当前线程的局部变量的副本就是这个计数值。
public class SeqCount {
private static ThreadLocal<Integer> seqCount = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
public int nextSeq() {
seqCount.set(seqCount.get() +1);
return seqCount.get();
}
public static void main(String [] args) {
SeqCount seqCount = new SeqCount();
SeqThread seqThread1 = new SeqThread(seqCount);
SeqThread seqThread2 = new SeqThread(seqCount);
SeqThread seqThread3 = new SeqThread(seqCount);
SeqThread seqThread4 = new SeqThread(seqCount);
seqThread1.start();
seqThread2.start();
seqThread3.start();
seqThread4.start();
}
public static class SeqThread extends Thread {
private SeqCount seqCount;
public SeqThread(SeqCount seqCount) {
this.seqCount = seqCount;
}
@Override
public void run() {
for (int i=0; i<3; i++) {
System.out.println(Thread.currentThread().getName()+" seqCount:"+seqCount.nextSeq());
}
}
}
}
4、源码地址
网友评论