ThreadLocal的原理
ThreadLocal类结构:
首先关注 ThreadLocal的属性:
/** * ThreadLocals rely on per-thread linear-probe hash maps attached * to each thread (Thread.threadLocals and * inheritableThreadLocals). The ThreadLocal objects act as keys, * searched via threadLocalHashCode. This is a custom hash code * (useful only within ThreadLocalMaps) that eliminates collisions * in the common case where consecutively constructed ThreadLocals * are used by the same threads, while remaining well-behaved in * less common cases. */ private final int threadLocalHashCode = nextHashCode();
threadLocalHashCode:threadLocal对象的hashCode,一个ThreadLocal对象都有一个属于自己的hashCode,不同的ThreadLocal对象的hashCode各不相同,在set和get ThreadLocalMap的时候起到hash的作用。
/** * The next hash code to be given out. Updated atomically. Starts at * zero. */ private static AtomicInteger nextHashCode = new AtomicInteger(); /** * Returns the next hash code. */ private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); }
nextHashCode:一个静态变量,和nextHashCode()配合使用,给每个创建的对象分配hashCode
/** * The difference between successively generated hash codes - turns * implicit sequential thread-local IDs into near-optimally spread * multiplicative hash values for power-of-two-sized tables. */ private static final int HASH_INCREMENT = 0x61c88647;
HASH_INCREMENT:生成ThreadLocal对象时hashCode的增量,也是用于生成hashCode的
以上三个变量都是用来生成hashCode的,用于在ThreadLocalMap中进行快速查找。
使用ThreadLocal的两个重要方法:
void set(T value) & T get()set方法:
public void set(T value) { // 获取当前线程 Thread t = Thread.currentThread(); // 获取当前线程的 ThreadLocalMap ThreadLocalMap map = getMap(t); if (map != null) // 插入hash表 map.set(this, value); else // 第一次使用,创建 hash 表并且插入entry createMap(t, value); } private void set(ThreadLocal<?> key, Object value) { // hash散列表的底层实现是数组 Entry[] tab = table; int len = tab.length; // 计算 hash int i = key.threadLocalHashCode & (len-1); // 处理 hash 冲突并插入Entry for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<?> k = e.get(); if (k == key) { e.value = value; return; } if (k == null) { replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); } // 这里解决hash冲突用的是线性探测法 private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); }
get方法:
public T get() { // 获取当前线程 Thread t = Thread.currentThread(); // 获取当前线程的 ThreadLocalMap ThreadLocalMap map = getMap(t); if (map != null) { // 查Hash表 ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); } // 获取key对应的entry private Entry getEntry(ThreadLocal<?> key) { // 计算 hash 值 int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) // hash 命中,返回结果 return e; else // hash 未命中 return getEntryAfterMiss(key, i, e); }
ThreadLocal的常见用法:保存用户信息,在调用时可以不用传参,直接get
使用方法 :
setp1:new ThreadLocal对象:
package com.dianping.utils; import com.dianping.dto.UserDTO; public class UserHolder { private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>(); public static void saveUser(UserDTO user){ tl.set(user); } public static UserDTO getUser(){ return tl.get(); } public static void removeUser(){ tl.remove(); } }
step2:使用set方法
UserHolder.saveUser((UserDTO) userDTO);
step3:使用get方法
// 获取登录用户 UserDTO user = UserHolder.getUser();