结构
适用场景
需要是当前线程资源持有的
- 多个应用需要访问同一个线程持有的变量
- 比如可以通过 ThreadLoalMap 拿用户数据
需要线程资源保持一致性
- 比如 jdbc 控制同一个线程的事务中的数据库连接是同一个连接
线程安全
- 避免线程不安全,因为 ThreadLocal 是当前线程才持有
分布式计算
- 可以将需要计算的部分分分给多个线程计算,然后汇集计算结果
注意事项
- tomcat 会重用线程
- ThreadLocal 会被重用,如果是存储当前用户,需要先干掉
API
简单使用
class TL {
public static ThreadLocal<Integer> local = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
System.out.println("initial value!");
return 1;
}
};
public static void main(String[] args) {
// 会初始化值
local.get();
// 不会初始化值
local.get();
// 干掉 local
local.remove();
// 会初始化值
local.get();
}
}
减少同步/伪分布式
- 比如访问接口的自增一计算
- 如果要用
synchroinized
来修饰共享变量,会造成 qps 减低- 当然可以用
Atomic
类来搞,这里只是说一下如何减少同步
- 当然可以用
- 可以统计各个线程的访问当前接口的访问量
@RestController
@RequestMapping("/lo")
public class TreadLocalController {
/**
* 保存各个线程的 Val
* 而各个 Val 保存是当前线程的 ThreadLocal 的值
*/
static HashSet<Val<Integer>> saveSet = new HashSet<>();
/**
* 避免线程不安全
* 由于在 initalValue() 中才会使用到,所以被执行的次数是 <线程总数>
*/
private synchronized void setValue(Val<Integer> val) {
saveSet.add(val);
}
/**
* 当前线程 ThreadLocal
*/
private ThreadLocal<Val<Integer>> threadLocal = new ThreadLocal<Val<Integer>>() {
@Override
protected Val<Integer> initialValue() {
System.out.println(Thread.currentThread().getName() + "- 初始化了" );
// 由于只能拿当前线程的值,干脆把数据存到另一个结构中,引用扔进 set
Val<Integer> val = new Val<>();
val.set(0);
// HashSet 线程不安全
setValue(val);
return val;
}
};
@RequestMapping("/add")
public String add() {
// 给 threadLocal 自增一
Val<Integer> cur = threadLocal.get();
cur.set(cur.get() + 1);
return "success";
}
@RequestMapping("/get")
public String get() {
Integer sum = 0;
for (Val<Integer> val : saveSet) {
sum += val.get();
}
String str = "总数: " + saveSet.size() + ": 值: " + sum;
return str;
}
}
/**
* 存储 ThreadLocal 的值,因为返回 ThreadLocal 本身有点难度
* @param <T>
*/
class Val<T> {
private T t;
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
}