线程之ThreadLocal

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

引用分类

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

House house=new House();

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

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

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

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

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

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

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

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

   House house=new House();
   PhantomReference reference=new PhantomReference(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> {
            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 {
 
    public static  ThreadLocal withInitial(Supplier var0) {
        return new ThreadLocal.SuppliedThreadLocal(var0);
    }
}

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

2.脏数据

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

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

你可能感兴趣的:(线程之ThreadLocal)