Java并发知识ThreadLocal

2020-05-16  本文已影响0人  only_one
image
ThreadLocal简介:
ThreadLocal是一个线程内部的数据储存类,为每个线程都提供了变量的副本,使得每个线程在某一时间訪问到的并非同一个对象,这样就隔离了多个线程对数据的数据共享。
ThreadLocal的使用:
ThreadLocal类中有几个重要方法:
1、void set(T value);设置当前线程的线程局部变量的值:
 public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
ThreadLocal中存储数据调用的是set(T value)方法(T是什么?泛型)
2、public Object 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();
    }
如果当前线程获取到的局部变量为空,那么就会走到 setInitialValue();方法 那我们看一下这个方法里面到底做了那些操作:
 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;
    }
代码片段可以看出在setInitialValue()中做3个步骤:
(1)、调用initialValue()对value进行初始化:返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null
 protected T initialValue() {
        return null;
    }
(2)、调用getMap(t);通过当前线程对象去获取ThreadLocalMap对象
  ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
ThreadLocal.ThreadLocalMap threadLocals = null;
(3)、根据判断条件执行map.set(this, value);或者createMap(t, value);
map.set(this, value)
    private void set(ThreadLocal<?> key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            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();
        }
注意到这里TheradLocal是key,而非Thread,映射关系也并非保存在ThreadLocal中,而是保存在每个Thread中。ThreadLocal所关心的仅仅是自己的hash值和弱引用。Thread内部保存着这个内部类的引用。
image.png
createMap(t, value)
 void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
在上面的方法中可以看出返回ThreadLocalMap 瞅瞅源码:
image.png
可以看到有个Entry内部静态类,它继承了WeakReference,总之它记录了两个信息,一个是ThreadLocal<?>类型,一个是Object类型的值。getEntry方法则是获取某个ThreadLocal对应的值,set方法就是更新或赋值相应的ThreadLocal对应的值。
image.png
image.png
回顾我们的get方法,其实就是拿到每个线程独有的ThreadLocalMap然后再用ThreadLocal的当前实例,拿到Map中的相应的Entry,然后就可以拿到相应的值返回出去。当然,如果Map为空,还会先进行map的创建,初始化等工作。
最后举个简单例子:
// 给每个线程进行了隔离, 相当于 给每一个线程 Copy了一份副本一样
public class TestThreadLocal {

    static ThreadLocal<String> threadLocal = new ThreadLocal<String>() {
        @Override
        protected String initialValue() {
            return "雄霸";
        }
    };

    private static class StudentThread extends Thread {

        @Override
        public void run() {
            super.run();
            // 首次拿 雄霸
            // 非常明确 1.获取当前是那个线程   2.ThreadLocalMap
            threadLocal.get(); // 雄霸
            String threadName = currentThread().getName();
            threadLocal.set("步惊云");
            // get 永远都是再 StudentThread 线程修改的的
            System.out.println("threadName:" + threadName + " get:" + threadLocal.get()); // 步惊云
        }
    }

    private static class WorkderThread extends Thread {

        @Override
        public void run() {
            super.run();
            String threadName = currentThread().getName();
            System.out.println("threadName:" + threadName + " get:" + threadLocal.get()); // 雄霸
        }
    }

    public static void main(String[] args) {
        new StudentThread().start();
        new WorkderThread().start();

        String threadName = Thread.currentThread().getName();
        System.out.println("threadName:" + threadName + " get:" + threadLocal.get());
    }
}
上一篇 下一篇

猜你喜欢

热点阅读