本篇文章主要讲述ThreadLocal是如何实现线程本地变量的。<br />参考:冰河-高并发笔记<br /> CodeSheep-[https://mp.weixin.qq.com/s/ND-nUCGvXTHkEClKqN1qrQ](https://mp.weixin.qq.com/s/ND-nUCGvXTHkEClKqN1qrQ)
概述
解决线程安全问题的方法主要有阻塞同步、非阻塞同步和无同步方法(深入理解JVM 3版)
- 阻塞同步:synchronized和ReentrantLock
- 非阻塞同步:CAS
- 无同步方法:使用ThreadLocal
ThreadLocal将变量私有化,于是线程间没有共享数据,就不存在什么线程安全问题了
ThreadLocal原理探究
- 线程独享的变量存储在哪里?
- ThreadLocal的set()、get()、remove()方法的源码
- 如何让子线程获取父线程中设置的值
线程独享变量存储在哪里?
答:变量存储在该线程的Thread类实例中。
public class Thread implements Runnable {
// 省略代码....
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
// 省略代码....
}
- ThreadLocal可以看成一个工具类
- 具体的作用就是操作当前线程的私有变量
ThreadLocal的方法源码分析
set()方法
```java public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null)
elsemap.set(this, value);
}createMap(t, value);
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
- 当前线程调用 `ThreadLocal.set(xxx)`
- `set()`方法获取当前是哪个线程调用的(这里调用线程假设为A)
- 从线程A中获取它的`ThreadLocal.ThreadLocalMap threadLocals`的值
- 为空,调用`createMap()` 为线程A创建一个
- 不为空,修改原`threadLocals`成员变量的值
<a name="XQGlJ"></a>
### get()方法
```java
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
- 线程A调用
ThreadLocal.get()
方法 - 获取当前调用线程(这里线程为线程A)
- 从线程A中获取
threadLocals
成员变量 - 若
threadLocals
不为空且threadLocals
(可以看作一个Map)的里面值也不为空,则从中获取值 否则就是初始化这个
threadLocals
并设置一个this->null
键值对remove()方法
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
线程A调用
ThreadLocal.remove()
- 获取当前调用线程(线程A)的
threadLocals
-
子线程获取父线程的值
通过
ThreadLocal
设置的值是不具备传递效果的,但是可以使用其子类InheritableThreadLocal
来实现值的传递。InheritableThreadLocal的原理探究
父线程在
new Thread
之前通过InheritableThreadLocal.set()
设置值可以被新new出来的这个线程所拿到,并复制到新线程中public class Thread implements Runnable {
// 省略代码....
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
// 省略代码....
}
在Thread中还有一个和
threadLocals
类型一致的成员变量inheritableThreadLocals
- 通过
ThreadLocal
设置的线程变量存储在threadLocals
中 通过
InheritableThreadLocal
设置的线程变量存储在InheritableThreadLocals
中方法探究
这里我就不列举
InheritableThreadLocal
的set()
、get()
和remove()
方法的具体代码了。
该类是ThreadLocal
的子类,上述的三个方法均是ThreadLocal
类的实现,唯一不同的是InheritableThreadLocal
重写了其中获取调用线程中的ThreadLocal.ThreadMap
的方法。ThreadLocal
获取且操作threadLocals
InheritableThreadLocal
则是操作inheritableThreadLocals
如何传递
本质:将父线程的
inheritableThreadLocals
成员变量赋值给子线程的inheritableThreadLocals
- 发生位置:
new Thread()
的构造方法执行过程中完成注意
作者认为,在new完子线程后,父线程对inheritableThreadLocals
变量的修改失不会影响子线程的
ThreadLocal内存泄漏
待补充