ThreadLocal与InheritableThreadLocal 的理解
ThreadLocal 的类图结构
ThreadLocalMap是一个定制化的HashMap,默认每个线程中的两个变量threadlocals和inheritableThreadLocals都是null ,只有当前线程第一次调用了Threadlocal的set和get方法才会进行创建。
/* 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;
每个先后才能的本地变量不是存放到ThreadLocal实例里面的,而是存放到调用线程的threadLocals变量里面
再看看ThreadLocal的set、get方法
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();
}
getMap
// 每个线程有个成员变量 threadLocals
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
初始化map
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;
}
// 创建map
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
查看set方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
可以发现ThreadLocal和hashmap比较起来,特殊的是 数组元素 不是一个列表或者红黑树, 而就是单独的Entry的值
public class ThreadLocalTest2 {
// (1)打印函数
static void print(String str) {
// 1.1 打印当前线程本地内存中localVariable变量的值
System.out.println(str + ":" + localVariable.get());
// 1.2 清除当前线程本地内存中localVariable变量
// localVariable.remove();
}
// (2) 创建ThreadLocal变量
static ThreadLocal<String> localVariable = new ThreadLocal<>();
public static void main(String[] args) {
// (3) 创建线程one
Thread threadOne = new Thread(new Runnable() {
public void run() {
// 3.1 设置线程one中本地变量localVariable的值
localVariable.set("threadOne local variable");
// 3.2 调用打印函数
print("threadOne");
// 3.3打印本地变量值
System.out.println("threadOne remove after" + ":" + localVariable.get());
}
});
// (4) 创建线程two
Thread threadTwo = new Thread(new Runnable() {
public void run() {
// 4.1 设置线程one中本地变量localVariable的值
localVariable.set("threadTwo local variable");
// 4.2 调用打印函数
print("threadTwo");
// 4.3打印本地变量值
System.out.println("threadTwo remove after" + ":" + localVariable.get());
}
});
// (5)启动线程
threadOne.start();
threadTwo.start();
}
}
每个线程内部是有一个 threadlocal的成员变量,其中key是我们定义的threadlocal的this引用,value是我们设置的值,每个线程的本地变量是存到线程自己的内存变量的threadlocals里面,当线程一直不消失的时候,这些本地变量会一直存在。所以有可能造成内存泄露。 使用完本地变量 ,可以调用ThreadLocal的remove方法删除对应线程的threadLocals中的本地变量。
如何理解线程内部 这个threadlocal成员变量呢 ?看下面的代码加深理解
public class TestThreadLocal3 {
// (1) 创建线程变量
public static ThreadLocal<String> threadLocal = new ThreadLocal<String>();
public static void main(String[] args) {
// (2) 设置线程变量
threadLocal.set("hello world");
// (3) 启动子线程
Thread thread = new Thread(new Runnable() {
public void run() {
// (4)子线程输出线程变量的值
System.out.println("thread:" + threadLocal.get());
}
});
thread.start();
// (5)主线程输出线程变量值
System.out.println("main:" + threadLocal.get());
}
}
除了ThreadLocal外,还有一个InheritableThreadLocal 这个类是继承 ThreadLocal的
public class InheritableThreadLocal<T> extends ThreadLocal<T> {}
这个类和ThreadLocal 不一样的地方是采用 inheritableThreadLocals 代替了ThreadLocal
InheritableThreadLocal 重写了createMap方法
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
查看Thread类的成员变量
/* 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 类的构造函数
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* Do we have the required permissions?
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
// 创建inheritableThreadLocals 这个变量
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
看createInteritedMap内部实现
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}
这个类 有啥用,其实就是把父线程中的变量给子类用
InheritableThreadLocal 类通过重写代码让本地变量保存到了具体线程的 inheritableThreadLocals 变量里面,线程通过 InheritableThreadLocal 类实例的 set 或者 get 方法设置变量时候就会创建当前线程的 inheritableThreadLocals 变量。当父线程创建子线程时候,构造函数里面会把父线程中 inheritableThreadLocals 变量里面的本地变量拷贝一份复制到子线程的 inheritableThreadLocals 变量里面。
看实现代码
public class TestThreadLocal3 {
// (1) 创建线程变量
// public static ThreadLocal<String> threadLocal = new ThreadLocal<String>();
// 将父线程中的变量给子线程
public static ThreadLocal<String> threadLocal = new InheritableThreadLocal<String>();
public static void main(String[] args) {
// (2) 设置线程变量
threadLocal.set("hello world");
// (3) 启动子线程
Thread thread = new Thread(new Runnable() {
public void run() {
// (4)子线程输出线程变量的值
System.out.println("thread:" + threadLocal.get());
}
});
thread.start();
// (5)主线程输出线程变量值
System.out.println("main:" + threadLocal.get());
}
}