zoukankan      html  css  js  c++  java
  • ThreadLocal使用与原理探索

    ThreadLocal原理探索

    ThreadLocal与synchronized的区别是:

    ThreadLocal:每个线程单独有一个副本

    synchronized:共用同一个资源,加锁

    显然ThreadLocal是用空间换时间

    ThreadLocal大致存储是这样的:

    1. 每个线程有一个ThreadLocalMap,即使不同的ThreadLocal实例,只要是同一个线程他们共享的都是同一个ThreadLocalMap
    2. ThreadLocalMap以ThreadLocal实例为key,且设置key为弱引用进行存储
      1. 设置key为弱引用的好处是,我不用这个ThreadLocalMap的时候,我只需要显示将变量赋值为null,因为它没有强引用了,我还设置它是弱引用,那GC就直接回收了,这样就不会造成内存泄漏的问题。【当然如果线程迟迟不结束,如果这个ThreadLocal对象还被被回收了,那么Map中key虽然没有了,但是value还在,而这个Map的生命周期和线程生命周期一致,因此我们当前线程不使用ThreadLocal了,就调用其remove放法,进行释放,否则仍然会造成内存泄漏】
    3. 当然如果我们只是当前线程不需要使用ThreadLocal了,那么我们在当前线程直接手动调用remove()方法即可
      image-20210203082439254

    接下来我试着剖析一波

    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,如果没有创建一波,它是以当前ThreadLocal实例对象为key进行存储

    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,如果有则获取当前首先获取当前线程的ThreadLocal实例对象的Entry,然后取出值,否则调用方法和set源代码一样,只不过设置默认值null

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

    可以看出,首先获取当前线程的ThreadLocalMap,然后移除当前ThreadLocal实例对象为key的value

    整个测试

    里面许多的注释是从最初探讨的时候写的,有的不一定对,以我上访分析源码为准

    /**
     * @author ningxinjie
     * @date 2021/2/3
     */
    // https://blog.csdn.net/weixin_42388901/article/details/96491793
    public class ThreadLocalTest {
        // 测试下来可以看到,threadLocal果然是内部按照线程为key,每个线程有各自的ThreadLocalMap,本线程直接取出来的就是本线程的ThreadLocalMap;
        // 其中remove针对的也是本线程的,因此本线程不使用的时候最好手动调用一下,因为ThreadLocalMap的key是弱引用
    
    
        // 每个ThreadLocal实例,内部为每个线程创建一个ThreadLocalMap,这个Map以当前ThreadLocal实例为key,且内部存放key是弱引用
        // 为什么设置成弱引用呢?这样我们不用这个对象的时候直接将这个对象=null即可,如果设置成强引用,那么内部的map的key是这个对象。这个对象一直是key存放在map中
    
        // 	ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用引用他,那么系统gc的时候,这个ThreadLocal势必会被回收,
        // 	这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:
        static ThreadLocal threadLocal = new ThreadLocal(); // 目前我们不把它设置为null,他就是强引用,因此目前不会被回收
        static ThreadLocal threadLocal2 = new ThreadLocal(); // 都讲jdk建议设置成private static这样强引用就不会被回收,我们不用手动置null,这样没有了强引用就变成了弱引用,gc即回收
    
        public static void main(String[] args) throws Exception {
            threadLocal.set("this is main");
            new Thread(new Runnable() {
                @Override
                public void run() {
                    threadLocal.set("this is thead01");
                    System.out.println("thread01 set ok");
                    System.out.println(threadLocal.get());
                }
            },"thead01").start();
            Thread.sleep(200);
    
            // 同一个线程共用同一个ThreadLocalMap,但是key是ThreadLocal实例 ,这里的实例是threadLocal2 ,因此没有值
            System.out.println(threadLocal2.get());
            Object o = threadLocal.get();
            System.out.println(o);
    
            // 移除当前线程的ThreadLocalMap
            threadLocal.remove();
            System.in.read();
        }
    
  • 相关阅读:
    直接选择排序(C++模版技术实现)
    求素数
    快速排序(C++模版技术实现)
    堆排序(C++模版技术实现)
    简单链式二叉树(C++模版技术实现)
    归并排序(C++模版技术实现)
    求斐波那契数列的两种解法
    C++中改变setw(n)的对齐方式
    C中的64位整型
    Windows版GCC之TDMGCC 4.5.2
  • 原文地址:https://www.cnblogs.com/ningxinjie/p/14370384.html
Copyright © 2011-2022 走看看