java中ThreadLocal使用

一、简介
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 ThreadLocal stuCtx = 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,相互间不干扰。




 

你可能感兴趣的:(java,java多线程)