ThreadLocal

面试准备,无法保证理解正确性,慎,欢迎纠正

ThreadLocal

  • 为什么需要threadlocal

    • 在多线程程序中,线程安全是最重要的一点,其实也就是保证共享变量被合理的共享或保证非共享变量不被共享,threadlocal是解决后者的.最常用的是web服务器中,当多个用户并发访问,某个用户信息肯定不能被其他用户线程获取,这就需要使用threadlocal保存每个用户线程的信息,互不干扰,线程运行结束后对该线程的变量副本进行回收,除非该副本有别的引用.
    • 使用时声明Threadlocal对象为private static
  • 实现原理

    • 首先看一下一个方法,public static native Thread currentThread();,看到native就可以将这个方法实现细节看做黑盒,然后一看名字,发现很常见,就是获取当前线程.如果我们在多线程的程序中,使用这个方法获取到每一个执行的线程,然后在线程内部存储每个线程运行内容的信息.不就是ThreadLocal的实现吗
    • 实际上就是这样的.Thread内部有一个静态内部类ThreadLocalMap,其内部还有一个内部类Entry,是一种数据类型,是一种键值对的数据结构,key是ThreadLocal,值是Object类型,这就很明了了,ThreadlocalMap类型就是一个类似HashMap的映射存储结构,后续的过程也就很清楚了
      • Threadlocal对象通过Thread.currentThread获取当前Thread对象
      • 当前Thread对象通过对象获取内部持有的ThreadLocalMap容器
      • 从ThreadLocalMap容器中用ThreadLocal对象作为键,保存线程信息为value.
    • 那么ThreadLocal是怎样的结构呢?需要注意的是,ThreadLocal并不是一个容器,当我们使用ThreadLocal的set(value)方法时,其实是先获取到该线程内部的ThreadLocalMap,将threadLocal作为键,要存储的值作为值存放在这个ThreadLocalMap中;当使用get()方法时,还是先获取当前线程的ThreadLocalMap,将threadlocal实例作为键,从map中拿到保存的值.
    • 可能上面的描述很饶人,我们从宏观的角度再看一遍.造成很绕的原因是对ThreadLocal的初始认知,我们可能认为ThreadLocal为每个线程保存了一个副本.这个理解其实是不对的,从我们上面的描述来看,ThreadLocal只是把自己"复制"一份交给每个线程,存放在每个线程内部的ThreadLocalMap中,还是那句话,threadlocal不是容器,它只是一个工具.
  • Threadlocal引起的内存泄漏

    • 很有名的一本书《Effective Java》第二版中的第6条:消除过期的对象引用.具体的内容是,我们在使用存放对象的数组时,如果我们删除操作只是数组长度size–,而没有将对应位置的引用赋值null,这个对象引用就不会被gc回收,如果程序很大,会造成内存泄漏.
    • ThreadLocal中也是这样的问题,如果我们在使用玩ThreadLocal后,没有将每个线程内部的ThreadLocalMap中的Entry对象消除,它就会一直存在(只要线程存在),便会有内存泄漏的危险.
    • 解决,ThreadLocal提供了remove方法,使用完调用即可.
    • 这里有个弱引用的问题,我在别的博客说过弱引用,单说不好理解,要结合四种引用类型说,看一下,弱引用会在下一次gc时回收.然后我们看一下ThreadLocalMap内部Entry的源码:
static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

可以看到,ThreadLocal,也就是键,是弱引用类型,那就很奇怪了,如果是弱引用类型,为什么还会造成内存泄漏呢,不是会自动回收呢,事实上,弱引用的确会被回收,但是只是回收threadlocal,而保存在map中的Entry实例还是存在,而回收后的键是null,但实例还是存在,所以会造成内存泄漏问题.

  • InheritableThreadLocal
    • 有一个问题,如果一个线程的子线程想要获取父线程保存在ThreadLocal的值怎么办,要知道,尽管是父子线程,但毕竟还是两个线程,用传统的ThreadLocal自然是不行,就要使用InheritableThreadLocal.
    • 具体的实现可以自行寻找,实现上有差别,但是设计理念上没有变.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值