ThreadLocal理解

1.ThreadLocal的类层级结构

  • ThreadLocal理解_第1张图片

2.ThreadLocal作用

  • ThreadLocal修饰的变量:使得每个线程都拥有该变量的本地副本。(你有你的,我有我的,你修改你的并不会影响我的变量的值)

3.ThreadLocal使用场景

  • 适用于高并发场景下,各个线程通过不同变量值完成操作的场景。

4.源码解析

  • set(T value) 
    • ThreadLocal理解_第2张图片
  •  get()
    • ThreadLocal理解_第3张图片
  • remove() 
    • ThreadLocal理解_第4张图片

5.内存溢出

  • static class Entry extends WeakReference> {
                /** The value associated with this ThreadLocal. */
                Object value;
    
                Entry(ThreadLocal k, Object v) {
                    super(k);
                    value = v;
                }
            }

     由于新创建的Entry的key它是弱引用类型,value是强引用。GC收回的时候弱引用key会被GC回收,而value由于是强引用,因此不会被GC回收,从而可能发生内存泄露。

    解决方法:手动调用该ThreadLocal的remove方法手动清除value的引用,tab[staleSlot].value = null; tab[staleSlot] = null。删除该Entry。

6.心得:

  • ThreadLocal.set(T value):实际是调用底层的ThreadLocalMap进行操作的,实际存放数据的是Entry数组。
  • ThreadLocal的 set/get/remove方法都会去删除掉key为null的Entry。

7.疑惑点:

  • 为什么不直接用线程id来作为ThreadLocalMap的key?

    • 如果采用线程id来作为key的话,那么就没法存放多个值,第二次及以后的set其实就是update操作。但是采用ThreadLocal作为key,一个Thread的ThreadLocalMap就可以存放多个值。实际跟底层set(T value)源码有关。

  • ThreadLocal里面如何解决hash冲突的?

    • e = tab[i = nextIndex(i, len)]; 通过线性探测法来查找下一个位置,如果e不为空,那么证明发生冲突,冲突的位置的值用旧值或新值替换。如果没有协议发生冲突(e为空),直接就创建一条新的Entry插入到Entry数组里面。
      
  • ThreadLocal里面的Entry为什么使用弱引用?

    • 如果使用强引用的话,及时把ThreadLocal设置成null,但是由于Entry里面还保存着该ThreadLocal(作为Entry的key),GC回收的时候,发现Entry强引用该ThreadLocal,因此不会被GC回收,从而发生内存泄露。
    • 如果使用弱引用的话,把上面的key设置为null,会被GC回收,它是尽可能保证不要出现内存泄露。

你可能感兴趣的:(Java,基础知识,ThreadLocal)