一、简介
ThreadLocal是java线程中的局部变量,变量作用域仅在当前线程有效,是线程安全的。
ThreadLocal常用于在多线程中,各自线程保存自己私有的变量值,如会话管理等场景。
二、ThreadLocal常用方法
public class ThreadLocal
protected T initialValue():初始化方法,protected方法,用于子类重写;
public void set(T value):设置值;
public T get():获取值;
public void remove():清空值。当线程回收时,局部变量也会自动回收,主动调起是非必须操作,只是加快回收速度;
三、原理
ThreadLocal在多线程中是线程安全的,原因是在每个线程中,都维持着自己私有的变量threadLocals,
参考Thread类关键源码:
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null;
而上面Thead类中的threadLocals变量的维护是在ThreadLocal类中,TheadLocal类的关键源码如下:
/** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @return the current thread's value of this thread-local */ public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); } /** * Variant of set() to establish initialValue. Used instead * of set() in case user has overridden the set() method. * * @return the initial value */ private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }
/** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */ public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
/** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @return the map */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; } /** * Create the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @param firstValue value for the initial entry of the map */ void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
在上面ThreadLocal类中,可以发现,ThreadLocalMap的key为当前的threadLocal实例,value为设置的值 。ThreadLocalMap 其实是维护着一个数组变量,关键源码如下:
/** * The table, resized as necessary. * table.length MUST always be a power of two. */ private Entry[] table;
再往下走,Entry其实是一个软引用,源码如下:
/** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. */ static class Entry extends WeakReference> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal> k, Object v) { super(k); value = v; } }
到此,ThreadLocal能实现线程安全,私有,脉络很清晰了。实质是,变量存在于各个线程空间内存中,
各自调自己的内存空间变量,互不影响。
四、示例
import lombok.Data; import org.apache.commons.lang3.RandomUtils; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class ThreadLocalStudy { public static void main(String[] args) { //线程一,定义自己的变量001 Runnable t1 = () -> { try { StuContext.get().setStuNo("001"); System.out.println("t1: set stuNo 001" ); TimeUnit.SECONDS.sleep(RandomUtils.nextInt(0, 3)); System.out.println("t1: get " + StuContext.get().getStuNo()); } catch (InterruptedException e) { }finally { StuContext.stuCtx.remove(); System.out.println("t1: remove stuCtx" ); } }; //线程一,定义自己的变量002 Runnable t2 = () -> { try { StuContext.get().setStuNo("002"); System.out.println("t2: set stuNo 002" ); TimeUnit.SECONDS.sleep(RandomUtils.nextInt(0, 3)); System.out.println("t2: get " + StuContext.get().getStuNo()); } catch (InterruptedException e) { }finally { StuContext.stuCtx.remove(); System.out.println("t2: remove stuCtx" ); } }; //使用线程池执行线程t1,t2 ExecutorService executor = Executors.newCachedThreadPool(); executor.submit(t1); executor.submit(t2); executor.shutdown(); } /** * 定义stu上下文类 */ @Data public static class StuContext { public static ThreadLocalstuCtx = new ThreadLocal<>(); private String stuNo; /** * 获取上下文 * @return */ public static StuContext get() { if (null == stuCtx.get()) { //初始化上下文 stuCtx.set(new StuContext()); } return stuCtx.get(); } } }
输出:
t2: set stuNo 002
t1: set stuNo 001
t2: get 002
t2: remove stuCtx
t1: get 001
t1: remove stuCtx
结果分析:从上面输出可以看出,t1和t2线程在各自的线程内操作TheadLocal变量stuCtx,相互间不干扰。