ThreadLocal:java.lang.ThreadLocal
应用:应用在同一个线程的不同开发层次中共享数据。例如:Struts2中的ActionContext、Spring中管理数据库的连接、Hibernate中的Session
实现:
1)建立一个类,并在其中封装一个静态的ThreadLocal变量,使其成为一个共享的数据环境。
2)在类中实现访问静态ThreadLocal变量的静态方法(设值和取值)
【说明】:ThreadLocal通过操作当前线程中的一个内部变量(ThreadLocalMap)来达到与其它线程隔离的目的。
1)ThreadLocalMap变量属于线程的内部属性,不同的线程拥有完全不同的ThreadLocalMap变量
2)线程中的ThreadLocalMap变量的值是在ThreadLocal对象进行set或get操作时创建的,在创建之前会检查当前线程中的ThreadLocalMap是否为null,如果为null则创建,如果不为null,则使用已经存在的ThreadLocalMap
3)使用当前线程的ThreadLocalMap的关键在于:使用当前的ThreadLocal的实例作为key进行储存
【重要】:
1)一个ThreadLocal只能储存一个变量,如果重复调用ThreadLocal的set方法,则新值会将旧值覆盖。
2)如果需要在一个线程中共享多个变量,则可以将多个变量封装到一个对象中,然后将该对象存储在ThreadLocal中。Struts2中的ActionContext就是这样设计的。
3)在ThreadLocalMap中,如果当前线程中定义了多个不同的ThreadLocal的实例,则它们会作为不同的key进行储存而不会相互干扰!
java.lang.Thread的部分源码:
public class Thread implements Runnable {
//这里省略了许多其它的代码
ThreadLocal.ThreadLocalMap threadLocals = null;
}
java.lang.ThreadLocal的部分源码:
public Class ThreadLocal<T> {
//这里省略了许多其它的代码
/**
* ThreadLocalMap is a customized hash map suitable only for maintaining thread local values.
* No operations are exported outside of the ThreadLocal class.
* The class is package private to allow declaration of fields in class Thread.
*
* # 用于处理生命周期很长的场景,hash表中entry的key被弱引用。
* To help deal with very large and long-lived usages, the hash table entries use WeakReferences for keys.
*
* However, since reference queues are not used, stale entries are guaranteed to be removed only when the table starts running out of space.
*/
static class ThreadLocalMap {
/**
* Entry继承了弱引用
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
// private T referent; // Entry继承了WeakReference的referent属性。
Object value;
// 堆内存中的threadlocal对象k,被Entry对象弱引用。Entry对象通过referent属性引用着threadlocal对象k
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
/**
* Set the value associated with key.
*
* @param key the thread local object
* @param value the value to be set
*/
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
// key被弱引用
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash();
}
}
// 将value的值保存在当前线程的本地变量表(ThreadLocalMap)中
public void set(T value){
// 获取当前线程
Thread t = Thread.currentThread();
// 调用getMap方法获得当前线程中的本地变量表ThreadLocalMap
ThreadLocalMap map = getMap(t);
// 如果本地变量表ThreadLocalMap已经存在,则直接使用
if (map != null){
// 以当前的ThreadLocal的实例作为key,储存在当前线程中。ThreadLocalMap
// ThreadLocalMap中,如果当前线程中定义了多个不同的ThreadLocal的实例,则它们会作为不同的key进行储存而不会相互干扰
// ThreadLocalMap的set方法:private void set(ThreadLocal<?> key, Object value)
map.set(this, value);
} else {
// 如果ThreadLocalMap不存在,则初始化map
createMap(t, value);
}
}
// 获取当前线程中以当前ThreadLocal实例为key的变量值
public T get() {
// 获取当前线程
Thread t = Thread.currentThread();
// 获取当前线程中的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
// 获取当前线程中以当前ThreadLocal实例为key的变量值
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
return (T)e.value;
}
}
// 当map不存在时,初始化map,并返回null
return setInitialValue();
}
// 从当前线程中获取与之对应的ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadlocals;
}
// 创建当前线程中的ThreadLocalMap
void createMap(Thread t, T firstValue) {
// 调用构造函数生成当前线程中的ThreadLocalMap
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
// 初始化当前线程的ThreadLocalMap
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;
}
// 若当前线程的ThreadLocalMap不存在时,调用ThreadLocal的get()方法默认返回null。
// 若子类覆盖父类ThreadLocal的该方法,则可以设置当(当前线程的)ThreadLocalMap不存在时,get()方法默认返回的值。
protected T initialValue() {
return null;
}
// ThreadLocalMap的定义
static class ThreadLocalMap{
// ...
}
}
应用举例:Struts2中的ActionContext
ActionContext在内部封装了一个静态的ThreadLocal的实例,而这一实例操作的对象()又是ActionContext本身。
Struts2中的ActionContext的部分源码:
public class ActionContext implements Serializable {
// 此处省略了很多代码
// 封装了一个ThreadLocal变量,储存的内容是ActionContext本身
static ThreadLocal actionContext = new ThreadLocal();
// 在ThreadLocal中设置ActionContext,绑定到当前线程。
public static void setContext(ActionContext context){
actionContext.set(context);
// 注:ThreadLocal的set方法调用了ThreadLocalMap(当前线程的本地变量表)的set(threadLocal, value)方法:以当前的ThreadLocal的实例作为key,储存在当前线程的ThreadLocalMap属性中
}
// 返回当前线程中储存的ActionContext
public static ActionContext getContext(){
// 返回当前线程中储存的ActionContext
return (ActionContext) actionContext.get();
}
}
ThreadLocal - 基础
于 2020-03-05 21:34:20 首次发布