1 总概
1 解决哪些问题
2 内存溢出
threadLocal 相当于将每个线程都开启了一个内存空间,用于存储每个线程独有的数据,每个线程只需操作自己的那个数据即可。 threadLocal 里面有 成员变量ThreadLocalMap <线程,数据>;
但是key是弱引用关系, 这个key可能会被GC回收,一旦被回收,这个key就会变成null,这个数据就会无法访问, 当我们操作数据得时候,threadlocal会默认帮助我们做一些清理的操作,就是把key为null的数据清理。但是依然可能会发生内存溢出
1 每次调用set方法时,也找到合适的时候一定要 .remove()方法清除数据
2 介绍
线程安全:每个线程作为key 数据作为value,所以当前线程获取的永远是自己的那个数据
private static ThreadLocal threadLocal = new ThreadLocal();
private static ConcurrentHashMap hashMap = new ConcurrentHashMap();
public static void main(String[] args) {
hashMap.put(Thread.currentThread(), "123");
threadLocal.set("456");
Object o = threadLocal.get();
Object o1 = hashMap.get(Thread.currentThread());
System.out.println(o);//456
System.out.println(o1);//123
hashMap.remove(Thread.currentThread());
threadLocal.remove(); //必须用完删除否则内存溢出
}
2场景
1 多线程下某个东西不是线程安全的
2 每个线程保存一份自己的数据,无锁竞争
重点:
1 同一个服务下的上下游是同一个线程,可以使用TreadLocal,controller->service->dao
2 不同服务的上下游不是同一个线程(跨服务),不可以使用TreadLocal
2.1 数据库连接
1 更新用户 和更新订单,必须在同一个连接,提交事务,才能一起回滚。否则将是各自提交各自的事务。
2.2 时间格式化
initialValue(); 兜底操作,
当前线程没有去执行set(),不去set数据时,当执行到get(),时就会触发 initialValue();
如果线程执行了set(),当执行到get(),时不会触发 initialValue();
static ThreadLocal<SimpleDateFormat> local= new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue(){
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//线程没有set值时,,get()就会触发执行一次
return simpleDateFormat;
}
};
private static ThreadLocal<SimpleDateFormat> simpleDateFormat = ThreadLocal.withInitial(() ->
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")//每次get()都会执行一次
);
@Test
void test3() {
for (int i = 0; i < 20; ++i) {
Thread thread = new Thread(() -> {
try {
// 当前线程不去set数据时,当执行get()方法时,就会执行 initialValue()中的代码,进行兜底,获取数据
local.set(new SimpleDateFormat("yyyy-MM-dd"));
System.out.println(local.get().parse("2022-08-25 10:00:00"));
} catch (ParseException e) {
e.printStackTrace();
} finally {
//TODO 线程运行结束清除,避免内存泄露
local.remove();
}
});
thread.start();
}
}
2.3 存放用户信息等
https://blog.csdn.net/u013728021/article/details/78719504
两种泛型方式写法 可以添加任何对象
public class ThreadLocalDemo<T> {
//当这个类加载到内存的的时候,静态属性赋值,所以只会new 一次这个对象,
public static ThreadLocalDemo INSTANCE = new ThreadLocalDemo();
private ThreadLocal<T> local = null;
private ThreadLocalDemo() {
System.err.println("================================走了无参构造===================");
local = new ThreadLocal<T>();
}
public T get() {
return (T)local.get();
}
public void set(T input) {
local.set(input);
}
public void remove() {
local.remove();
}
}
public class ThreadLocalDemo2<T> {
public static ThreadLocalDemo2 INSTANCE = null;
static {
//先走静态属性赋值 再走静态代码块
System.err.println("================================static===================");
INSTANCE = new ThreadLocalDemo2();
}
private ThreadLocalDemo2() {
System.err.println("================================走了无参构造===================");
local = new ThreadLocal<T>();
}
private ThreadLocal<T> local = null;
public T get() {
return (T)local.get();
}
public void set(T input) {
System.err.println(Thread.currentThread().getName());
local.set(input);
}
public void remove() {
local.remove();
}
}
存object
public class ThreadLocalDemo3 {
public static ThreadLocal local = new ThreadLocal();
public Object get() {
return local.get();
}
public void set(Object input) {
local.set(input);
}
public void remove() {
local.remove();
}
}
指定具体类型方式
public class ThreadLocalDemo3 {
public static ThreadLocal<Order> local = new ThreadLocal();
public Order get() {
return local.get();
}
public void set(Order input) {
local.set(input);
}
public void remove() {
local.remove();
}
}