-
使用场景
-
在进行对象跨层之间的数据传递时,使用ThreadLocal可以避免多次传递,打破层次间的约束;
-
通过ThreadLocal可以做线程间的数据隔离;
-
通过ThreadLocal可以进行事务操作,用于存储线程事务信息;
-
可以进行数据库的连接,以及Session会话的处理;
-
需要注意的地方
-
ThreadLocal主要用于解决多线程中数据因并发产生的不一致的问题。
-
ThreadLocal为每个线程中的并发访问单独提供一个副本,通过访问副本来运行业务,减少了线程同步带来的资源消耗,减少了线程并发控制的复杂度每个线程都有一个与之关联的ThreadLocalMap(类似Map),map中保存了一个键值对Entry(key,value),其中这个key是ThreadLocal 的弱引用,value是我们要操作的变量值;
-
在ThreadLocal对象使用完毕后,应该要把这个Entry对象进行回收,但是ThreadLocalMap是通过强引用指向的Entry对象,无法通过GC进行垃圾回收,所以就需要我们在使用的过程当中,手动的把它回收掉,也就是执行remove();
-
需要注意的是,ThreadLocal在执行get(),set()以及remove()方法的过程中,会把key为空的Entry对象的value也置为空,从而达到回收无用的对象;
-
在进行get之前,必须先set,否则会报空指针异常,如果想在get之前不需要调用set就能正常访问的话,必须重写initialValue()方法。
Demo:
/**
* 不安全的日期格式化工具类
*/
public class DataUtilNotSafe {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static Date parse(String dataStr) {
Date date = null;
try {
date = sdf.parse(dataStr);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
/**
** 不安全的日期格式化工具类会报线程错误
**/
public class ThreadLocalTest {
public static void main(String[] args) {
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5,
7,
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(10));
for (int i = 0; i < 5; i++) {
poolExecutor.execute(()->{
System.out.println(DataUtilNotSafe.parse("2022-7-29 16:43:30"));
});
}
poolExecutor.shutdown();
}
}
/**
* 安全的日期格式化工具类
*/
public class DataUtilSafe {
//通过ThreadLocal避免了线程之间的冲突
private static final ThreadLocal<SimpleDateFormat> THREAD_LOCAL = ThreadLocal.withInitial(
() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
);
public static Date parse(String dataStr) {
Date date = null;
try {
date = THREAD_LOCAL.get().parse(dataStr);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
public class ThreadLocalTest {
public static void main(String[] args) {
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5,
7,
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(10));
for (int i = 0; i < 5; i++) {
poolExecutor.execute(()->{
System.out.println(DataUtilSafe.parse("2022-7-29 16:43:30"));
});
}
poolExecutor.shutdown();
}
}
使用ThreadLocal修饰的工具类再去进行多个线程时间的日期格式化操作时,就不会出现上面的错误了:(ThreadLocal会让多个线程之间互相隔离)如下:
创作不易,点个赞再走呗