ThreadLocal原理分析

ThreadLocal总体介绍

​ ThreadLocal类在并发编程中,每个线程实例,都有一份独立的副本,采用以空间换时间的方式,处理并发。

在Thread类中,ThreadLocal.ThreadLocalMap threadLocals = null;ThreadLocal.ThreadLocalMap就是线程变量的容器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  static class ThreadLocalMap {

/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;

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

​ 如上代码可以看到Entry继承了WeakReference,在构造函数中super(key),key就是指ThreadLocal引用本身this。跟WeakHashMap不同的是,ThreadLocal的构造函数,没有指定ReferenceQueue。ThreadLocal用一种不同的方式,来避免内存泄漏,在下面的章节中,会详细介绍ThreadLocal的方式。

​ ThreadLocal的API,就不在此阐述,很简单。

每个线程独立备份

​ 为了达到这个目的,有两个方案。

## 方案A

 ThreadLocal 维护一个 Map,键是 Thread,值是它在该 Thread 内的实例。增加线程与减少线程均需要写 Map,故需保证该 Map 线程安全。线程结束时,需要保证它所访问的所有 ThreadLocal 中对应的映射均删除,否则可能会引起内存泄漏。
  • 结论:加锁势必会降低ThreadLocal的性能。猜测JDK为了性能考虑,没有采用此方案

方案B

Map 由 Thread 维护,从而使得每个 Thread 只访问自己的 Map,那就不存在多线程写的问题,也就不需要锁。该方案虽然没有锁的问题,但是由于每个线程访问某 ThreadLocal 变量后,都会在自己的 Map 内维护该 ThreadLocal 变量与具体实例的映射,如果不删除这些引用(映射),则这些 ThreadLocal 不能被回收,可能会造成内存泄漏。
  • 结论:JDK采用了B方案,也有对应的方案来解决内存泄漏问题

#JDK如何解决内存泄漏问题

# java

评论