典型场景一:
每个线程需要一个独享的对象(通常是工具类,典型的类有 SimpleDateFormat 和 Random)
代码示例:
public class ThreadLocalNormalUsage05 {
public static ExecutorService threadPool = Executors.newFixedThreadPool(10);
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
int finalI = i;
threadPool.submit(new Runnable() {
@Override
public void run() {
String date = new ThreadLocalNormalUsage05().date(finalI);
System.out.println(date);
}
});
}
}
public String date(int secods) {
Date date = new Date(1000 * secods);
SimpleDateFormat dateFormat = ThreadSafeFormatter.dateFormatThreadLocal.get();
return dateFormat.format(date);
}
}
class ThreadSafeFormatter {
public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
}
};
}
典型场景二:
每个线程内需要保存全局变量(例如在连接器中获取用户信息),可以让不同方法直接使用,避免参数传递的麻烦。
这些信息在同一个线程内相同,但是在不同的线程内是不同的,所以不能使用 static 方法来解决。使用 ConcurrentHashMap,synchronized 等会对性能有影响。
代码示例:
public class ThreadLocalNormalUsage06 {
public static void main(String[] args) {
new Service1().process();
}
}
class Service1 {
public void process() {
User user = new User("小张");
UserContextHolder.holder.set(user);
new Service2().process();
}
}
class Service2 {
public void process() {
User user = UserContextHolder.holder.get();
System.out.println("service2" + user.name);
new Service3().process();
}
}
class Service3 {
public void process() {
User user = UserContextHolder.holder.get();
System.out.println("service3" + user.name);
UserContextHolder.holder.remove();
}
}
class UserContextHolder {
public static ThreadLocal<User> holder = new ThreadLocal<>();
}
class User {
String name;
public User(String name) {
this.name = name;
}
}
总结
ThreadLocal 有 set 和 initialValue 两种方法保存对象,当我们能够控制对象初始化时机的时候可以用 initialValue。当我们控制不了,需要某些条件触发的时候可用 set。
使用 ThreadLocal 的四点好处:
- 线程安全
- 不需要加锁,提高执行效率
- 高效使用内存(对象生成),节省开销。
- 免去传参的麻烦,如场景二。
每一个线程都有一个 ThreadLocalMap,这里面存放的是 ThreadLocal,如果在使用线程池的环境下,不停的向 ThreadLocalMap 中新增 ThreadLocal,那么极有可能引发内存泄露,所以在最后调用完的时候需要记得删除ThreadLocal。如 Service3 中的示例。
网友评论