zoukankan      html  css  js  c++  java
  • ThreadLocal 面试

    问:

      说一下 ThreadLocal 原理, java8 

    答:

      java8, 每个线程对应的 Thread 对象内部有一个 ThreadLocals 字段, 这个字段指向堆中的一个ThreadLocalMap.

      这个 ThreadLocalMap 内部存储的是,当前线程与其他 ThreadLocal 对象关联的数据。

      Thread 这个线程对象,里面有一个 map 对象,这个map 存的是ThreadLocal 对象关联的数据。

    问:

      它是怎么做到线程,互不干扰的。

    答:

      线程有一个自己的 THreadLocalMap 存储数据。

      线程访问某个 ThreadLocal 对象 get 方法时,会检测 当前线程 map 内部是否有 key 为这个 ThreadLocal 对象的 Entry 数据。

      如果没有,这个 ThreadLocal 的 initial Value 方法 会创建一个 Entry 然后存放到这个 ThreadLocalMap 

    问:

      jdk 1.8 之前的 版本怎么设计的

    答:

      老版本会在,TreadLocal 里面维护一个大 map, 所有线程的变量都会维护在一个 map 里面。

    问:

      jdk 8 和 之前的版本有什么优势。

    答:

      老版本维护一个大的 map, 线程多的话, 这个map 会很大。不利于维护。

      新的版本。每个线程都会维护自己的数据,当线程被销毁的时候,线程对应额 ThreadLocalMap 在下次 GC 的时候被回收了。

      还有这个 ThreadLocalMap 中的 Entry 存的 key 是弱引用,如果 ThreadLocal 对象被回收的话,是不影响的即弱引用不参与 root 算法。

    问:

      使用的 Hash 是从 Object 继承下来的 hashCode 方法吗?

    答:

      不是,这个是自己重写的,用一个黄金分割数来分割,均匀的分布在 Entry 数组里面。

      如果 从 Object 继承的 HashCode 计算出来的 hash 值是不均匀的。 如果用黄金分割数,分配 hash 值,映射到散列表内部就很均匀。

      比如长度为 16 分配四个,就 table[0] table[4] table[8] table[12], 反映到散列表,就很均匀。

    问:

      为什么 TheadLocalMap 使用 自定义 map, 而不是 jdk 的 HashMap

    答:

      重写的话,可以把这个 key 为限定为特有类型,就是 ThreadLocal 这个类型,key 是弱以用。

      TheadLocal 这个写数据和查数据过程中,有清理过期数据的策略。能够将过期数据清理掉,解决了内存泄漏问题。

        TheadLocal 的 value 的引用, 如果是对应的数据是过期的话,就会被干掉,   

     问:

      每个线程的 ThreadLocalMap 对象是什么时间创建的

    答:

      每个线程的 ThreadLocalMap 是延迟初始化的

      第一次调用get 或者 set 时候,检测当前线程是否绑定 ThreadLocalMap,

      如果有就继续 get 或者 set, 如果没有, 就先创建。

    问:

      那么这个线程会不会被多次创建?

    答:

      在线程的生命周期内,ThreadLocalMap 只会初始化一次

    问:

      这个 map  初始化长度是多少

    答:

      16

    问:

      为什么这个长度,是 2 的次方数

    答:

      和 hashMap 一样,方便 hash 寻址。 因为 2 的次方数减一之后转变为 二级制由 1 组成,

      如果数值与二进制位与运算,得到的数,大于等于0 且小于等于这个二进制数值,比取模算法,即%,效率高很多。

      即 因为使用的是 位运算,所以效率高。

    问:

      扩容阈值时多少, 它达到扩容阈值一定会扩容吗

    答:

      entry 数组的 2/3。

      但是不一定会扩容,它会 rehash 一次, 调用 rehash 方法。

      全量扫描整个散列表的逻辑,把过期数据清理掉,

      如果全量扫描完后,当前散列表的数据仍然达到这个扩容阈值 3/4, 才真正进行扩容.

    问:

      这个扩容算法是什么

    答:

      首先,创建一个新的数组,长度是当前散列表数组的两倍,迭代老的数组,将其中的数组,按照  hash 算法放入,新的数组里边。

      迭代完后,这个数组就迁移完了。然后更新 ThreadLocalMap 对象的散列表引用。它会指向这个新的数组引用,扩容基本完成。

      (细节) 扩容之后,还会重新计算下次扩容的阈值。

    问:

      ThreadLocal Map Get 的逻辑

    答:

      根据这个 ThreadLocal 对象的 hash 值 按位与  , 当前数组长度减一 得到一个 index 

      这个散列表数组中,下标就是这个 index 的元素,可能就是要查找的数据。如果查找的地方,发生过 hash 冲突,因为 ThreadLocal

      内部类,Entry 没有 next 这个字段,ThreadLocal 采用的是 hash冲突后,线性的找到一个合适的位置去写数据。

      如果 get 没有命中的话,就要继续向后查找,直到找到这个数据或者碰到 null 就结束。同时,还会遍历当前数据是否过期。

    问:

      假如第一次 get 没有 get 到, 如果查找过程中碰到过期数据,怎么处理

    答:

      首先,过期数据是什么。

     ThreadLocal 内部存的 是 Entry, Entry 有两个字段,分别是 key 和 value, key 是一个弱引用。指向内存,已经限定类型的 ThreadLocal 对象。

     value 就是当前线程的关联对象, 当 key 对应的 ThreadLocal 对象被 GC 回收后,

     以为 key 是 弱引用,所以 key 的 get 方法 可能会 get 一个 指向 null 的一个引用,就这个 Entry 是过期的。

        再说一下,get 查询过程,碰到 过期数据怎么处理

      先会触发 “探测式” 过期数据回收逻辑, 就是从当前桶位开始向后迭代, 碰到 key == null 的 Entry 设置为 nll,一直迭代到 slot == null 为止。

      向下迭代过程中如果遇到正常数据,会根据 key 重新重新计算一个 index, 如果等于,index 是否等于 当前位置,如果等于,就相当于啥也不做, 

      因为写入时,可以认为没有发生过 hash 冲突。如果重新计算的 index 不等于当前位置,说明发生了 hash 冲突, 当前数据的slot之前可能 有过期数据被干掉。

      正常数据需要重新寻找一个更合适的位置去存放数据,这个位置理论上更接近或等于于正确的 index。

    问:

      Set 的流程

    答:

        根据 key 找到 对应下标的 slot ,如果 slot 为 null , 说明当前 set 方法是,新添加数据的逻辑。

      如果这个 slot 不是 null, 那情况就比较复杂。两种情况

      第一种,添加新的逻辑,但是发生 hash 冲突, 就线性找到可以使用的 slot 然后插入。

      第二种,是更新的逻辑,如果过程查到 key 和 set 的 key 一致的话,发生Entry替换 value,

          如果查找到过期数据,就做一个替换逻辑。

    问:

      set 过程中,替换过期数据的逻辑是怎么样。这个挺难的,还记得吗

    答:

      它会以当前位置的下一个桶位开始向后去查找,直到碰到 null 或者 key  一致才会停止。

       第一种情况,就是碰到当key 一致的时候,那么set 的这个数据,直接就更新到当前这个桶位的这个Entry,就可以了,就更新逻辑。

            然后让,当前的 Entry 与过期的 slot 进行一次互换。

      第二种情况,遍历到null , 也没有找到key 一致的数据,

            那么直接在当前过期桶位直接重写一个Entry 就 ok了,相当于抹除过期数据,将新的数据放到这里。

            还涉及到启发式过期数据的清理的逻辑。

    https://www.bilibili.com/video/BV19C4y1W72V?t=519

      

      

  • 相关阅读:
    Code Forces Gym 100886J Sockets(二分)
    CSU 1092 Barricade
    CodeChef Mahesh and his lost array
    CodeChef Gcd Queries
    CodeChef GCD2
    CodeChef Sereja and LCM(矩阵快速幂)
    CodeChef Sereja and GCD
    CodeChef Little Elephant and Balance
    CodeChef Count Substrings
    hdu 4001 To Miss Our Children Time( sort + DP )
  • 原文地址:https://www.cnblogs.com/Jomini/p/13857837.html
Copyright © 2011-2022 走看看