此时情绪此时天,无事小神仙
好好生活,平平淡淡每一天

编辑

ThreadLocal

ThreadLocal(线程的隔离)

ThreadLocal 使每一个线程有独立的副本:它 提供了线程内存储变量的能力,这些变量不同之处在于每一个线程读取的变量是对应的互相独立的。通过get和set方法就可以得到当前线程对应的值。

ThreadLocal的应用场景:

在多线程并发情况下,有一个共享变量,不同线程设置不同值后,各线程只获取自己设置的值

代码演示:

public class ThreadLocalTest {

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

    /**
     * 测试线程,线程的工作是将ThreadLocal变量的值变化,并写回,看看线程之间是否会互相影响
     */
    private static class ThreadInfo extends  Thread{
        int num;
        public ThreadInfo(int num){
            this.num = num;
        }
        @Override
        public void run() {
            int tl = threadLocal.get();
            tl += num;
            threadLocal.set(tl);
            System.out.println(Thread.currentThread().getName()+"---threadLocal.get():"+threadLocal.get());
        }
    }

    public static void main(String[] args) {
        int times = 5;
        Thread[] threads = new Thread[times];
        for (int i = 0; i < times; i++) {
            threads[i] = new ThreadInfo(i);
        }
        for (int i = 0; i < times; i++) {
            threads[i].start();
        }
    }
}

ThreadLocal运行结果:

 

从上面代码可以看出每个线程持有一个ThreadLocalMap对象。每一个新的线程Thread都会实例化一个ThreadLocalMap并赋值给成员变量threadLocals,使用时若已经存在threadLocals则直接使用已经存在的对象

每一个线程获取的ThreadLocal都是独立的。

无ThreadLocal的代码演示:

public class NoThreadLocal {
    static int noThreadLocal = 1;

    private  static class ThreadInfo extends Thread{
        public int num;
        public ThreadInfo(int num){
            this.num =  num;
        }
        @Override
        public void run() {
            noThreadLocal = noThreadLocal + num;
            System.out.println( Thread.currentThread().getName()+"---:"+noThreadLocal);
        }
    }

    public static void main(String[] args) {
        int times = 5;
        ThreadInfo[] threads = new ThreadInfo[times];
        for (int i = 0; i < times; i++) {
            threads[i] = new ThreadInfo(i);
        }
        for (int i = 0; i < times; i++) {
            threads[i].start();
        }
    }
}

无ThreadLocal运行结果:

ThreadLocal的实现原理:

ThreadLocal的set方法:第一步是获取当前线程实例,然后通过getMap方法获取当前线程实例的threadLocals属性,threadLocals是ThreadLocalMap类型的变量,而ThreadLocalMap则是一个定制化的HashMap。

源码注释:将此线程局部变量的当前线程副本设置为指定值。大多数子类无需重写此方法,只需依赖{@link#initialValue}方法来设置线程局部变量的值。

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

如果getMap方法获取的ThreadLocalMap类型变量map不等于null,则以当前ThreadLocal实例对象为key,传入的value值为value存到这个ThreadLocalMap中,需要注意的是在实际存储的时候,key使用的是ThreadLocal的弱引用。

如果getMap方法获取的ThreadLocalMap类型变量map等于null,则调用createMap方法创建一个ThreadLocalMap实例对象,并以当前ThreadLocal实例对象为key,传入的value值为value存到这个ThreadLocalMap中。

看到这里就会明白,使用ThreadLocal时,每个线程维护一个ThreadLocalMap映射表,映射表的key是ThreadLocal实例,并且使用的是ThreadLocal的弱引用 ,value是具体需要存储的Object。

ThreadLocal的get方法:第一步是获取当前线程实例,然后获取当前线程副本中指定的值,如果getMap方法获取到的ThreadLocalMap对象实例等于null,则调用setInitialValue方法初始化一个ThreadLocalMap,并以当前ThreadLocal实例对象为key,null值为value存到这个ThreadLocalMap中,同时返回null。

源码注释:返回此线程局部变量的当前线程副本中的值。如果变量没有当前线程的值,则首先将其初始化为调用{@link#initialValue}方法返回的值。

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

ThreadLocalMap map = getMap(t)的底层方法:重点注意这里是弱引用

源码注释:此哈希映射中的条目使用它的主ref字段作为键(它总是线程本地对象)。注意空键(即entry.get()==null)表示不再引用密钥,因此条目可以从表中删除。这些条目被提及作为下面代码中的“陈旧条目”

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

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

setInitialValue方法:返回ThreadLocal的初始值

源码注释:用于建立初始值的set()的变量。取而代之以防用户重写set()方法

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

流程图说明:Thread类中声明的threadLocals变量是map结构,每个线程都可以关联很多个ThreadLocal变量。

 未完待续...

参考网址:

https://www.jianshu.com/p/3c5d7f09dfbd

https://blog.csdn.net/windrui/article/details/105132387?depth_1-utm_source=distribute.pc_relevant.none-task-blog-OPENSEARCH-2&utm_source=distribute.pc_relevant.none-task-blog-OPENSEARCH-2

posted @ 2020-04-18 00:12  踏步  阅读(152)  评论(0编辑  收藏  举报