前言
这篇文章将介绍面试中也经常见到的另一重点——ThreadLocal。本文将分以下几个方面介绍:
- ThreadLocal 的使用场景以及使用方法
- ThreadLocal 的实现原理
- ThreadLocal 面试常见的问题
文章将尽量用通俗的语言来进行介绍,希望对读者有所帮助。
ThreadLocal 的使用场景以及使用方法
/**
* This class provides thread-local variables. These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* {@code get} or {@code set} method) has its own, independently initialized
* copy of the variable. {@code ThreadLocal} instances are typically private
* static fields in classes that wish to associate state with a thread (e.g.,
* a user ID or Transaction ID).
* /
上面的一段话是 ThreadLocal 官方给出的注释,从这段话中和 ThreadLocal 命名上也能看得出来,ThreadLocal 的主要是封装变量起到线程隔离的作用,另外 ThreadLocal 通常会用 private 和 static 来修饰,这个后文会解释原因。
首先介绍一下 ThreadLocal 的使用场景,根据 ThreadLocal 线程隔离的特性,主要有以下几种场景:
- 保存一个多层调用的参数。想象一下这样的场景,有一个变量,需要通过很多层调用才能最终使用到,但是这中间的每一层方法调用都需要传递这个变量作为方法的入参,这样写虽然可以达到目的,但是却不够美观,如果可以用到 ThreadLocal,随用随取,就会优雅很多。
- 保存上下文信息,如知名的 APM 系统 SkyWalking 就用到了 ThreadLocal 来保存 context,传递调用信息,还有 log4j 的 MDC 机制,也是基于 ThreadLocal 来实现的。
- 由于 ThreadLocal 保存的变量是每个线程私有的,所以也可以一定程度上保证线程安全,比如 SimpleDateFormat 就是线程不安全的,所以生产环境上就经常使用 ThreadLocal 来封装。
至于 ThreadLocal 的用法也很简单,下面给出一个简单的使用案例。
public class UserContext {
private static ThreadLocal threadLocal = new ThreadLocal();
public static User getUser() {
return (User) threadLocal.get();
}
public static void setUser(User user) {
threadLocal.set(user);
}
public static void remove() {
threadLocal.remove();
}
}
主要是三个方法,set,get 以及 remove。不要忘记 remove,不然容易造成内存泄漏。
ThreadLocal 的实现原理 ThreadLocalMap
ThreadLocal 是基于 ThreadLocalMap 实现的,而 ThreadLocalMap 是一个类似 map 结构,没有实现 map 接口,但是同样会产生 Hash 冲突,不同的是 HashMap 才用的是链地址法,即数组加链表, 而 ThreadLocalMap 采用的是开放地址法,当发生 hash 冲突的时候,按照某种方法继续探测哈希表中的其他存储单元,直到找到空位置为止,这里就不做深入解释了。
网友评论