相关文章:
ThreadLocal,用于提供线程局部变量,常用于多线程环境下对非线程安全资源的并发访问,如果我们不想加锁,就可以使用 ThreadLocal 来使得每个线程都持有一个该资源的副本,各个线程之间互不干扰
ThreadLocal 与各种锁机制相比,侧重于以空间换时间,来提高并发效率
一、内部类解析
-
SuppliedThreadLocal<T>
static final class SuppliedThreadLocal<T> extends ThreadLocal<T> { // 供给函数 private final Supplier<? extends T> supplier; SuppliedThreadLocal(Supplier<? extends T> supplier) { this.supplier = Objects.requireNonNull(supplier); } // 获取初始值 @Override protected T initialValue() { return supplier.get(); } }
- SuppliedThreadLocal 是 ThreadLocal 的内部类,其继承自 ThreadLocal,用于从指定的供给函数中获取初始值
-
ThreadLocalMap
-
ThreadLocalMap 是 ThreadLocal 的内部类,是一个自定义的哈希映射,用于维护线程的局部变量
-
ThreadLocalMap 所有方法的访问权限都是私有的,因此只有 ThreadLocal 可以调用 ThreadLocalMap 中的方法,而其他类则不可以
-
ThreadLocalMap 的访问权限是包级别的,也就是同一个包下面的类可以访问 ThreadLocalMap,但是不能调用 ThreadLocalMap 中的方法
-
二、字段解析
-
threadLocalHashCode
private final int threadLocalHashCode = nextHashCode();
- ThreadLocal 的哈希值,通过调用
nextHashCode()
方法计算得到
- ThreadLocal 的哈希值,通过调用
-
nextHashCode
private static AtomicInteger nextHashCode = new AtomicInteger();
- 下一个哈希值,使用 AtomicInteger 类进行原子更新
-
HASH_INCREMENT
private static final int HASH_INCREMENT = 0x61c88647;
-
哈希值增量,每当创建一个 ThreadLocal,
nextHashCode
就会增长魔数0x61c88647
-
魔数
0x61c88647
十分特殊,被称之为斐波那契数,或黄金分割数,用于使散列更加均匀,减少哈希冲突public static void main(String[] args) { long l1 = (long) ((1L << 32) * (Math.sqrt(5) - 1)/2); System.out.println("32 bit unsigned: " + l1); int i1 = (int) l1; System.out.println("32 bit signed: " + i1); System.out.println("0x61c88647 = " + 0x61c88647); // 32 bit unsigned: 2654435769 // 32 bit signed: -1640531527 // 0x61c88647 = 1640531527 }
-
如上所示,0x61c88647 = 2^32 * 0.618 (有符号 int)
-
有关魔数
0x61c88647
的相关知识,可以看这里:从 ThreadLocal 的实现看散列算法
-
-
举例说明
public class ThreadLocalTest { private static final int INITIAL_CAPACITY = 16; private static final int HASH_INCREMENT = 0x61c88647; private static AtomicInteger nextHashCode = new AtomicInteger(); private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); } private static void hashCodeTest(int len) { for (int i = 0; i < len; i++) { int bucket = nextHashCode() & (INITIAL_CAPACITY - 1); System.out.println("【" + i + "】在桶中的位置:【" + bucket +"】"); } } public static void main(String[] args){ hashCodeTest(16); // 【0】在桶中的位置:【0】 // 【1】在桶中的位置:【7】 // 【2】在桶中的位置:【14】 // 【3】在桶中的位置:【5】 // 【4】在桶中的位置:【12】 // 【5】在桶中的位置:【3】 // 【6】在桶中的位置:【10】 // 【7】在桶中的位置:【1】 // 【8】在桶中的位置:【8】 // 【9】在桶中的位置:【15】 // 【10】在桶中的位置:【6】 // 【11】在桶中的位置:【13】 // 【12】在桶中的位置:【4】 // 【13】在桶中的位置:【11】 // 【14】在桶中的位置:【2】 // 【15】在桶中的位置:【9】 } }
- 如上所示,数据在哈希桶数组中散列地很均匀
-
三、构造方法解析
-
ThreadLocal()
public ThreadLocal() { }
- 无参构造
四、方法解析
1、nextHashCode() 相关方法
-
nextHashCode()
private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); }
- 计算下一个哈希值,使用 AtomicInteger 类进行原子更新
2、get() 相关方法
-
get()
public T get() { // 获取当前线程 Thread t = Thread.currentThread(); // 获取与 ThreadLocal 相关联的 ThreadLocalMap ThreadLocalMap map = getMap(t); if (map != null) { // 根据 key 获取 Entry 对象 ThreadLocalMap.Entry e = map.getEntry(this); // 如果 Entry 对象不为 null if (e != null) { @SuppressWarnings("unchecked") // 获取 value 并返回 T result = (T)e.value; return result; } } return setInitialValue(); }
-
getMap(Thread t)
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
- 获取与 ThreadLocal 相关联的 ThreadLocalMap
-
setInitialValue()
private T setInitialValue() { // 获取线程局部变量的初始值 T value = initialValue(); // 获取当前线程 Thread t = Thread.currentThread(); // 获取与 ThreadLocal 相关联的 ThreadLocalMap ThreadLocalMap map = getMap(t); // 如果 map 不为 null if (map != null) // 设置与 key 关联的 value map.set(this, value); // 如果 map 为 null else // 创建与 ThreadLocal 相关联的 ThreadLocalMap createMap(t, value); return value; }
- 设置线程局部变量的初始值
-
initialValue()
protected T initialValue() { return null; }
-
返回线程局部变量的初始值,默认返回 null
-
如果不希望线程局部变量的初始值为 null,我们可以通过继承 ThreadLocal 并重写
initialValue()
方法来设定初始值
-
-
createMap(Thread t, T firstValue)
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
- 创建与 ThreadLocal 相关联的 ThreadLocalMap
3、withInitial(Supplier<? extends S> supplier) 相关方法
-
withInitial(Supplier<? extends S> supplier)
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) { return new SuppliedThreadLocal<>(supplier); }
- 创建线程局部变量,变量的初始值由 Supplier 供给函数提供
4、set(T value) 相关方法
-
set(T value)
public void set(T value) { // 获取当前线程 Thread t = Thread.currentThread(); // 获取与 ThreadLocal 相关联的 ThreadLocalMap ThreadLocalMap map = getMap(t); // 如果 ThreadLocalMap 不为 null if (map != null) // 设置与 key 关联的 value map.set(this, value); // 如果 ThreadLocalMap 为 null else // 创建与 ThreadLocal 相关联的 ThreadLocalMap createMap(t, value); }
- 设置线程局部变量的初始值为指定 value
5、remove() 相关方法
-
remove()
public void remove() { // 获取与 ThreadLocal 相关联的 ThreadLocalMap ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) // 删除 key 对应的 Entry m.remove(this); }
- 删除线程局部变量的当前值
6、createInheritedMap(ThreadLocalMap parentMap) 相关方法
-
createInheritedMap(ThreadLocalMap parentMap
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) { return new ThreadLocalMap(parentMap); }
-
创建与 InheritableThreadLocal 相关联的 ThreadLocalMap
-
该方法仅在 Thread 类的构造方法中被调用
-
7、childValue(T parentValue) 相关方法
-
childValue(T parentValue)
T childValue(T parentValue) { throw new UnsupportedOperationException(); }
-
该方法在子类 InheritableThreadLocal 中被显示定义
-
此处定义主要是为在 ThreadLocalMap 构造方法
ThreadLocalMap(ThreadLocalMap parentMap)
中调用该方法提供方便,通过多态来直接调用子类 InheritableThreadLocal 中的childValue(T parentValue)
方法
-
五、内存泄漏问题
-
ThreadLocalMap 以 ThreadLocal 的弱引用作为 key,而弱引用在 GC 时会被回收,这样一来,ThreadLocalMap 中就会出现 key 为 null 的 Entry,此时我们就无法访问这些 key 为 null 的 Entry 的 value,如果线程生命周期很长,这些 value 就会不断积累,且不会被 GC 回收 (value 存在一条强引用链:Thread -> ThreaLocalMap -> Entry -> value),最终造成内存泄漏
-
因此想要正确地使用 ThreadLocal,避免内存泄漏,我们需要显示地调用
remove()
方法来清除数据
六、举例说明
-
ThreadLocalTest.java
public class ThreadLocalTest { private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); private static void threadLocalTest() { threadLocal.set(123); System.out.println(threadLocal.get()); threadLocal.remove(); } public static void main(String[] args){ threadLocalTest(); // 123 } }