• ThreadLocal是什么?谈谈你对他的理解


    1、ThreadLocal是什么

    从名字我们就可以看到ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。

    2、ThreadLocal怎么用

    public class TestTreadLocal {
        private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
    
        public static void main(String[] args) {
    
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    threadLocal.set("kkk");
                    String s = threadLocal.get();
                    System.out.println(s);
                     threadLocal.remove();
                }
    
            });
            Thread thread1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    threadLocal.set("iiiii");
                    String s = threadLocal.get();
                    System.out.println(s);
                     threadLocal.remove();
                }
            });
            thread.start();
            thread1.start();
        }
    }
    
    
    

    上述可以看到,不同的线程对共享的变量操作都互不影响

    3、ThreadLocal源码分析

    接下来我们看看threadlocal源码

    set()的源码

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

    jiexialo从set方法我们可以看到,首先获取到了当前线程t,然后调用getMap获取ThreadLocalMap,如果map存在,则将当前线程对象t作为key,要存储的对象作为value存到map里面去。如果该Map不存在,则初始化一个。
    接下来,看一下threadlocal源码

       static class ThreadLocalMap {
    
            /**
             * The entries in this hash map extend WeakReference, using
             * its main ref field as the key (which is always a
             * ThreadLocal object).  Note that null keys (i.e. entry.get()
             * == null) mean that the key is no longer referenced, so the
             * entry can be expunged from table.  Such entries are referred to
             * as "stale entries" in the code that follows.
             */
            static class Entry extends WeakReference<ThreadLocal<?>> {
                /** The value associated with this ThreadLocal. */
                Object value;
    
                Entry(ThreadLocal<?> k, Object v) {
                    super(k);
                    value = v;
                }
            }
    //省略
    

    可以看到ThreadloacalMap就是threadloacal的静态内部类,里面定义了一个entry保存数据,以threadloacal为key,我们设置的值value作为value
    getmap()源码

    
    //ThreadLocal.ThreadLocalMap threadLocals = null;
    
        ThreadLocalMap getMap(Thread t) {
            return t.threadLocals;
        }
    

    上述代码其实就是调用threadlocalmap
    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();
        }
    

    上述为get()源码,可以看出首先获取当前线程threadlocalmap,如果map不为null,则获取当前线程的entry。entry的值value就作为value返回
    那如果map为null则初始化一个值

        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);//创建一个map
            return value;
        }
    
       protected T initialValue() {
            return null;//null
        }
    

    remove()源码

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

    从源码可以看出,将当前线程作为可以移除。源码跟下去看看remove方法

    private void remove(ThreadLocal<?> key) {
                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)]) {
                    if (e.get() == key) {
                        e.clear();
                        expungeStaleEntry(i);
                        return;
                    }
                }
            }
    

    可以看出,remove会移除该线程的所有value

    总结:
    (1)每个Thread维护着一个ThreadLocalMap的引用

    (2)ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储

    (3)ThreadLocal创建的副本是存储在自己的threadLocals中的,也就是自己的ThreadLocalMap。

    (4)ThreadLocalMap的键值为ThreadLocal对象,而且可以有多个threadLocal变量,因此保存在map中

    (5)在进行get之前,必须先set,否则会报空指针异常,当然也可以初始化一个,但是必须重写initialValue()方法。

    (6)ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。

    OK,现在从源码的角度上不知道你能理解不,对于ThreadLocal来说关键就是内部的ThreadLocalMap。

    4、ThreadLocal内存泄漏问题

    上面这张图详细的揭示了ThreadLocal和Thread以及ThreadLocalMap三者的关系。

    1、Thread中有一个map,就是ThreadLocalMap

    2、ThreadLocalMap的key是ThreadLocal,值是我们自己设定的。

    3、ThreadLocal是一个弱引用,当为null时,会被当成垃圾回收

    4、重点来了,突然我们ThreadLocal是null了,也就是要被垃圾回收器回收了,但是此时我们的ThreadLocalMap生命周期和Thread的一样,它不会回收,这时候就出现了一个现象。那就是ThreadLocalMap的key没了,但是value还在,这就造成了内存泄漏。

    解决办法:使用完ThreadLocal后,执行remove操作,避免出现内存溢出情况。

  • 相关阅读:
    redis修改密码
    redis配置
    django中日志配置
    django中缓存配置
    navicat批量导入数据
    django添加REST_FRAMEWORK 接口浏览
    django验证码配置与使用
    LUA_OBJECT
    LUA comment
    lua-redis
  • 原文地址:https://www.cnblogs.com/qingfeng5438/p/13623546.html
Copyright © 2020-2023  润新知