美文网首页
线程之ThreadLocal

线程之ThreadLocal

作者: 呵呵_9e25 | 来源:发表于2019-04-15 18:27 被阅读0次

    在谈线程的ThreadLocal之前需要了解一下java的引用

    引用分类

    1.强引用:我们平时通过new一个对象产生的对象名就是一个强引用,这是我们用的最广泛的引用

    House house=new House();
    

    强引用对象是即使内存再吃紧而且GC Roots可大,垃圾回收都不会回收这个对象

    2.软引用:通过SoftReference进行对象指向

      House house=new House();
      SoftReference<House> reference=new SoftReference<House>(house);
    

    软引用会把对象放到垃圾回收的队列中,在系统产生oom内存不足之前`就有可能去回收对象, 以保证系统的正常运行。

    3.弱引用:比软引用更弱的引用,通过WeakReference进行对象指向

       House house=new House();
       WeakReference<House> reference=new WeakReference<House>(house);
    

    弱应用的对象是在垃圾回收的时候更容易被回收,就是可以减少对强应用对象的挟持,减少内存泄漏

    4.虚引用:最弱引用,通过PhantomReference进行对象指向

       House house=new House();
       PhantomReference<House> reference=new PhantomReference<House>(house);
    

    虚引用只要垃圾回收就是回收该引用指向的对象,用的比较少,一般只用于获取对象回收时系统发出的通知

    ThreadLocal

    前面我们列举完了java的四大引用类型,然后我们现在就可以开始分析ThreadLocal了

    ThreadLocal的作用

    多线程之间进行数据共享,当多个线程同时操作一个统一对象的变量时,会产生数据错乱问题,其实就是同步问题,而我们的ThreadLocal正是为了解决这个问题。
    ThreadLocal在每个线程中对该变量会创建一个副本,即每个线程内部都会有一个该变量,这样在同步的时候不同的线程操作的都是一个变量的副本,互相之间是不会有交集的。

    实现原理

    Thread->ThreadLocalMap->多个ThreadLocal变量
    上面这个链状关系就是ThreadLocal实现多线程并发问题的核心原理。

    我们简单解释一下
    就是一个线程里会有一个容器ThreadLocalMap 容器的代码长这样

    static class ThreadLocalMap {
           //每个线程都有一个存储变量副本的数组
            private ThreadLocal.ThreadLocalMap.Entry[] table;
          
            //以ThreadLocal的弱应用为键 值为变量副本
           static class Entry extends WeakReference<ThreadLocal<?>> {
                Object value;
    
                Entry(ThreadLocal<?> var1, Object var2) {
                    super(var1);
                    this.value = var2;
                }
            }
           
            //为线程添加变量副本
           private void set(ThreadLocal<?> var1, Object var2) {
                ...
                //给table数组元素赋值
                var3[var5] = new ThreadLocal.ThreadLocalMap.Entry(var1, var2);
                ...
    
            }
    }
          
    

    一个线程里可以有多个ThreadLocal对象,每个 ThreadLocal的都对应一个变量副本。
    通过这个东西我们可以为每个线程创建一个变量副本,只要通过这个ThreadLocal来查找,最后是存储在这个ThreadLocalMap 里面。

    然后重点是每个ThreadLocal都是一个弱引用,这样可以有效的避免内存泄漏。

    问题

    但是如果我们看过阿里的《码出搞笑 Java开发手册》的都应该知道,ThreadLocal有两个副作用,一个就是脏数据,另一个就是内存泄漏。

    1.内存泄漏

    那我们是不是打脸,是的,因为ThreadLocal在线程里面的声明是这样的

    public class ThreadLocal<T> {
     
        public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> var0) {
            return new ThreadLocal.SuppliedThreadLocal(var0);
        }
    }
    

    返回的是一个static关键字声明的ThreadLocal,那即使gc也不能释放这个对象。

    2.脏数据

    就是在线程复用的时候如果ThreadLocal没有进行重新赋值,就是没有调用它的set()方法的话,你通过这个ThreadLocal查找回来的还是老数据,这就是脏数据产生的源头

    解决方案:
    两个问题的解决方案都是及时的调用remove()方法清理即可

    相关文章

      网友评论

          本文标题:线程之ThreadLocal

          本文链接:https://www.haomeiwen.com/subject/mmmuwqtx.html