面试题答案
一键面试1. ThreadLocal基本原理
ThreadLocal为每个使用该变量的线程提供独立的变量副本,每个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
2. 实现线程隔离的方式
- 内部数据结构:ThreadLocal内部通过一个ThreadLocalMap来存储每个线程的变量副本。每个Thread对象内部都有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,当通过ThreadLocal的set方法设置值时,实际上是往当前线程的threadLocals这个ThreadLocalMap中存入键值对,键为当前ThreadLocal实例,值为设置的变量副本。
- 线程访问独立:不同线程访问同一个ThreadLocal实例的get方法时,会从各自线程的threadLocalMap中获取对应的值,因此各个线程之间的变量副本相互隔离,互不干扰。
3. 多线程环境下的存储结构
- ThreadLocalMap:它是ThreadLocal的内部静态类,采用类似HashMap的结构,但与HashMap不同,它没有继承自任何Map接口。ThreadLocalMap以ThreadLocal实例作为键,以设置的变量副本作为值。它使用开放地址法(而不是链表法)来解决哈希冲突。
- Entry类:ThreadLocalMap中的每个元素是一个Entry对象,Entry继承自WeakReference<ThreadLocal<?>>,即键(ThreadLocal实例)是弱引用。这样设计的目的是当ThreadLocal对象没有强引用指向它时,在下一次垃圾回收时,键(ThreadLocal实例)会被回收,避免内存泄漏。
4. 数据访问机制
- set方法:当调用ThreadLocal的set方法时,首先获取当前线程的ThreadLocalMap,如果不存在则创建一个。然后以当前ThreadLocal实例为键,要设置的值为值,将键值对存入ThreadLocalMap中。
- get方法:调用get方法时,同样先获取当前线程的ThreadLocalMap,然后以当前ThreadLocal实例为键从ThreadLocalMap中获取对应的值。如果获取的值为null,且当前ThreadLocal实例有设置初始值的逻辑(重写了initialValue方法),则会调用initialValue方法初始化值,并将其存入ThreadLocalMap。
- remove方法:调用remove方法时,会从当前线程的ThreadLocalMap中移除以当前ThreadLocal实例为键的键值对,有助于避免内存泄漏。