Java线程局部变量ThreadLocal

ThreadLocal基础

ThreadLocal类可以让每个线程绑定自己的值,它就像一个全局存放数据的盒子,盒子中可以存放每个线程的私有数据。

ThreadLocal类只有一个无参的构造函数,因此实例化ThreadLocal的方法为: new ThreadLocal();

threadLocal.get()方法,取当前线程存放在ThreadLocal里的数据;

threadLocal.set(T value)方法,设置当前线程在ThreadLocal里的数据;

threadLocal.remove()方法,移除当前线程在ThreadLocal里的数据;

threadLocal.initialValue(),返回当前线程在ThreadLocal里的初始值。

类InheritableThreadLocal可以在子线程中取得父线程继承下来的值:在创建子线程时,子线程会接收所有可继承的线程局部变量的初始值,以获得父线程所具有的值。通常,子线程的值与父线程的值是一致的;但是,通过重写这个类中的 childValue 方法,子线程的值可以作为父线程值的一个任意函数。

ThreadLocal源码分析

首先看ThreadLocal类的set方法:

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

可以看到它首先获取当前线程对象,然后获取线程对象的threadLocals属性,如果threadLocals为null,就新建一个ThreadLocalMap对象赋值给它,如果不为null,则直接往其上塞值。由此我们可以看到,ThreadLocal本身并不存储数据,数据存储在Thread对象上面。Thread的threadLocals属性是一个ThreadLocal.ThreadLocalMap类型的非静态属性,ThreadLocalMap的实现如下:

static class ThreadLocalMap {

        static class Entry extends WeakReference> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }
		...

可以看到ThreadLocalMap的Entry继承了WeakReference,它使用ThreadLocal对象作为key。

再来看ThreadLocal类的get方法:

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();
    }

同样获取当前线程对象,然后获取这个Thread对象的threadLocals,如果不为空,就根据当前ThreadLocal对象key获取值并返回,如果为空,那就设置并返回初始值。

ThreadLocal使用demo

ThreadLocal可以用来存储线程级上下文,一个demo如下:

import java.util.TimeZone;

public class MyContext {

    private ThreadLocal tenantSpaceId;

    private ThreadLocal userTimeZone;

    private static MyContext instance = new MyContext();

    private MyContext() {
        tenantSpaceId = new ThreadLocal<>();
        userTimeZone = new ThreadLocal<>();
    }

    public static MyContext getInstance() {
        return instance;
    }

    public void setTenantSpaceId(String tsi) {
        tenantSpaceId.set(tsi);
    }

    public String getTenantSpaceId() {
        return tenantSpaceId.get();
    }

    public void setUserTimeZone(TimeZone tz) {
        userTimeZone.set(tz);
    }

    public TimeZone getUserTimeZone() {
        return userTimeZone.get();
    }

    public void clear() {
        tenantSpaceId.remove();
        userTimeZone.remove();
    }

}

 

你可能感兴趣的:(java.concurrent)