之前分享过TheadLocal的创建销毁原则,并且使用代码进行了测试ThreadLocal内存泄露
使用ThreadLocal的目的:
1.又希望并发安全,又不想使用锁;
2.线程间隔离,且希望线程内任务获取的是同一个对象,如spring中事务模块将数据库连接放在ThreadLocal中;
那怎么使用才是比较安全的使用方式呢
根据生命周期划分,主要分为三种情况
1.与Thread生命周期相同
一般适用于线程不安全的工具类,比如SimpleDateFormat;
或者数据库连接之类的,在spring中有使用,不仅仅保证了并发安全,也保证了同一个线程拿到的同一个connection,来保证事务性;
放在
//使用ThreadLocal代替原来的new SimpleDateFormat
private static final ThreadLocal<SimpleDateFormat> dateFormatter = new ThreadLocal<SimpleDateFormat>(){
//放在initialValue中会在首次get的时候初始化
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
//使用时
dateFormatter.get().format(new Date());
2.与task生命周期相同
这里有两种使用方式
一种是嵌套方式
private static final ThreadLocal<Object> threadLocal = new ThreadLocal<Object>();
try{
threadLocal.set(new Object());
//这里做真的业务
doBiz();
}finally{
threadLocal.remove();
}
当然,不嵌套也是可以的,需要有明确的业务入口
private static final ThreadLocal<Object> threadLocal = new ThreadLocal<Object>();
//在业务开始前完成ThreadLocal的设置
threadLocal.set(new Object());
doBiz();
3.小于task生命周期
这种情况只能使用嵌套方式
但是由于线程内任何地方都可以设置该ThreadLocal
所以,一定要封锁住set的权限,只能在一个地方set,如果那里都能访问到并且set,就乱套了
当然,你有胆量的话,能整理清楚复杂的业务逻辑,保证变量不出问题,就大胆随意用吧
public class BusinessHandler {
private static ThreadLocal<String> name = new ThreadLocal<>();
private static void setName(String newName){
name.set(newName);
}
public static String getName(){
return name.get();
}
public void handle(){
String thisName = "new name";
setName(thisName);
doBiz();
}
}
//在其他地方使用该ThreadLocal
public void doBiz() {
// use name
BusinessHandler.getName();
}
网友评论