ThreadLocal小谈
ThreaLocal这个类型经常用来实现应用上下文,应用上下文可以多线程共享的。常常在面试中也能听到”请你谈谈ThreadLocal与多线程的应用?” 我认为ThreadLocal和多线程的应用基本没有什么关系,而是在单线程中的应用。因为在线程A中对ThreadLocal
操作,线程B根本是无法感知的。ThreadLocal无法在线程之间传递数据(具体分析ThreadLocal的源代码)。ThreadLocal用来隔离各个线程。
代码分析
描述Thread,ThreadLocal,ThreadLocal.ThreadLocalMap这3个类中的关键属性和方法,来简单分析ThreadLocal的原理。代码全部来自jdk1.7,省略了一部分代码,自己加了注释。
Thread类
class Thread{
//这个是每个线程相互独立的关键,线程之间不共享
//默认的访问权限,同包名可访问
ThreadLocal.ThreadLocalMap threadLocals = null;
}
ThreadLocal类及其静态内部类ThreadLocalMap
class ThreadLocal{
//对外提供的API看不出线程隔离,内部是取当前线程的ThreadLocalMap
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
//以ThreadLocal为key
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
//如果没有,就设置初始值null
return setInitialValue();
}
/**
* 获取当前线程的ThreadLocalMap,真正用来存储上下文的地方,每个线程相互独立
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
/**
* Variant of set() to establish initialValue. Used instead
* of set() in case user has overridden the set() method.
*
* @return the initial value
*/
private T setInitialValue() {
T value = initialValue();//初始值为null
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
/**
* 每次线程中的ThreadLocalMap都是new出来的,所以相互独立
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
//这个就像是map,当超过了阈值就要rehash
//key为ThreadLocal,线程内不同的ThreadLocal值,value也是不一样的。
static class ThreadLocalMap{
//entry继承自弱引用,可以被gc回收
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
/**
* The table, resized as necessary.
* table.length MUST always be a power of two.
*/
private Entry[] table;
}
}
测试代码
同一个ThreadLocal在不同的线程中相互独立。
public class ThreadLocalTester {
public static void main(String[] args) {
ThreadLocal threadLocal = new ThreadLocal<Integer>();
new Thread(new ThreadLocalHolder(threadLocal, false)).start();
new Thread(new ThreadLocalHolder(threadLocal, true)).start();
}
}
class ThreadLocalHolder implements Runnable {
private ThreadLocal<Integer> threadLocal;
private boolean dir;
public ThreadLocalHolder(ThreadLocal<Integer> thlocal, boolean dir) {
this.threadLocal = thlocal;
this.dir = dir;
}
/*
* (non-Javadoc)
*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
while (true) {
Integer integer = threadLocal.get();
if (null == integer) {
threadLocal.set(0);
} else {
int i = integer.intValue();
i = dir ? i - 1 : i + 1;
threadLocal.set(i);
}
System.out.println(Thread.currentThread().getName() + " i is: " + threadLocal.get());
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
特点分析
ThreadLocal
内部定义了一个静态类ThreadLocalMap
这个类专门用来存放信息(非静态的内部类能访问外部类的资源),Thread
中有个类型为ThreadLocalMap
的默认访问级别的成员变量threadlocals
,这以为着每个线程之间的threadLocals变量是相互独立的,而同一个线程内都是共享的- ThreadLocalMap key的类型为ThreadLocal,这点应该是为专门给ThreadLocal使用的,
ThreadLoaclMap
这个class的访问级别也是默认级别。放入别的key就会产生编译错误。 - 其实ThreadLocalMap里面的entry类型为entry(ThreadLocal, Object),
new ThreadLoacl()
出来的threadlocal都是不一样的(key不同了),意味着不同的threadlocal里面的value值都是不同,实现了一个线程中存储多个值。 - 一个static的ThreadLocal变量,在不同的线程中或者的值都是不同的。因为
ThreadLocal
是从不同的线程中取值(getMap(){t.threadLocalMaps})。厉害 - 一个ThreadLocal对应一个键值对,ThreadLocalMap中的key为
Threadlocal