• 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();
        }
    
  • 相关阅读:
    【HDOJ】2774 Shuffle
    【POJ】2170 Lattice Animals
    【POJ】1084 Square Destroyer
    【POJ】3523 The Morning after Halloween
    【POJ】3134 Power Calculus
    【Latex】如何在Latex中插入伪代码 —— clrscode3e
    【HDOJ】4801 Pocket Cube 的几种解法和优化
    【HDOJ】4080 Stammering Aliens
    【HDOJ】1800 Flying to the Mars
    SQL语法
  • 原文地址:https://www.cnblogs.com/ningxinjie/p/14370384.html
Copyright © 2020-2023  润新知