Java中的ThreadLocal

    变量值的共享可以使用public static变量的形式,所有线程可以共享一个变量。如果我们想实现一个线程都有自己的共享变量该如何解决呢,这里就要用到Java中的ThreadLocal类。

    类ThreadLocal主要解决的就是每个线程绑定自己的值,可以将ThreadLocal类比喻成全局存放数据的盒子,盒子中可以存储每个线程的私有数据

    我们先看下列代码

public static void main(String[] args) throws InterruptedException {
    ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };

    threadLocal.set(100);
    System.out.println("主线程中的值:"+threadLocal.get());
    Thread t = new Thread(){
        @Override
        public void run() {
            threadLocal.set(200);
            System.out.println("线程t中的值:"+threadLocal.get());
        }
    };

    t.start();
}

代码定义了一个ThreadLocal对象,然后在主线程中,给ThreadLocal对象放入100,而线程t则放入200,并打印。结果如下


可见,每个线程都取到了自己存入的那个值。那么它到底是怎么实现的呢,让我们来分析分析吧

首先先介绍一下ThreadLocal的一些主要方法

1、initalValue方法

protected T initialValue() {
    return null;
}

initalValue方法,表示默认值,可见是null,但为了安全起见,我们在初始化ThreadLocal对象时采用匿名内部类来重写这个方法,以防止空指针异常。也就是这样:

ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
    @Override
    protected Integer initialValue() {
        return 0;
    }
};

2、get方法

public T get() {
    //获取当前线程对象
    Thread t = Thread.currentThread();
    //取出线程类中的ThreadLocalMap
    ThreadLocal.ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocal.ThreadLocalMap.Entry e = map.getEntry(this);
        //如果Map不为空并且该线程确实存入了变量,则返回存入的变量
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    //Map为空,返回默认值
    return setInitialValue();
}

这里要说到,每个线程中都维护了一个ThreadLocalMap对象,这个Map就存储着你所存入的值,下面会说到。

3、set方法

public void set(T value) {
    //获取当前线程对象
    Thread t = Thread.currentThread();
    //根据线程获取线程中的ThreadLocalMap
    ThreadLocal.ThreadLocalMap map = getMap(t);
    //如果map不为空则直接存入值
    if (map != null)
        map.set(this, value);
    //如果map为空,则创建Map并存入值
    else
        createMap(t, value);
}

4、remove方法

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
}

remove是移除所存入的值

5、getMap方法

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

根据线程去获得线程中的ThreadLocalMap对象

我们可以打开Thread类源码

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

这就是Thread类中的ThreadLocalMap对象,默认为空。只有你在使用ThreadLocal为线程保存私有属性时才会创建。

6、createMap方法

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

将线程中的ThreadLocalMap对象实例化并写入存入值

接下来就要看存储数据的ThreadLocalMap

static class ThreadLocalMap {
    //Map节点,注意keyThreadLocal而不是Thread
    static class Entry extends WeakReference<ThreadLocal<?>> {
        Object value;
        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
    //构造方法
    ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
        table = new ThreadLocal.ThreadLocalMap.Entry[INITIAL_CAPACITY];
        int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
        table[i] = new ThreadLocal.ThreadLocalMap.Entry(firstKey, firstValue);
        size = 1;
        setThreshold(INITIAL_CAPACITY);
    }
}

Map很简单,只不过要注意的是Map的键是ThreadLocal而不是Thread。

总结

    1、ThreadLocal是为了解决每个线程绑定自己的值

    2、ThreadLocal默认值为null,所以有时候为了安全,初始化时使用匿名内部类来重写initalValue方法

    3、每个线程对象内部都维护了一个ThreadLocalMao对象

    4、ThreadLocalMap的键是ThreadLocal而不是Thread

    5、ThreadlLocal可能会发生内存泄漏,所以在使用完ThreadLocal之后调用remove方法清除数据。

猜你喜欢

转载自blog.csdn.net/yanghan1222/article/details/80216142