您的位置:首页 > 其它

ThreadLocal-分析-总结

2015-06-01 17:07 369 查看
ThreadLocal<T>类在Spring,Hibernate等框架中起到了很大的作用,对于其工作原理,很多网上的文章分析的不够彻底,甚至有些误解。

首先,为了解释ThreadLocal类的工作原理,必须同时介绍与其工作甚密的其他几个类(内部类)
1.ThreadLocalMap
2.Thread



可能有人会觉得Thread与ThreadLocal有什么关系,其实真正的奥秘就在Thread类中的一行:

Java代码


ThreadLocal.ThreadLocalMap threadLocals = null;

其中ThreadLocalMap的定义是在ThreadLocal类中,真正的引用却是在Thread类中

那么ThreadLocalMap究竟是什么呢?

可以看到这个类应该是一个Map,JDK的解释是

写道
ThreadLocalMap is a customized hash map suitable only for maintaining thread local values

接下来的重点是ThreadLocalMap中用于存储数据的entry

Java代码


static class Entry extends WeakReference<ThreadLocal> {

/** The value associated with this ThreadLocal. */

Object value;

Entry(ThreadLocal k, Object v) {

super(k);

value = v;

}

}

从中我们可以发现这个Map的key是ThreadLocal变量,value为用户的值,并不是网上大多数的列子key是线程的名字或者标识

到这里,我们就可以理解ThreadLocal究竟是如何工作的了

1.Thread类中有一个成员变量叫做ThreadLocalMap,它是一个Map,他的Key是ThreadLocal类
2.每个线程拥有自己的申明为ThreadLocal类型的变量,所以这个类的名字叫'ThreadLocal':线程自己的(变量)
3.此变量生命周期是由该线程决定的,开始于第一次初始(get或者set方法)
4.由ThreadLocal的工作原理决定了:每个线程独自拥有一个变量,并非共享或者拷贝

Java代码


/**

* @author mxdba

*

*/

public class ThreadLocalSample {

public static void main(String[] args) {

ThreadTest test1 = new ThreadTest(10);

ThreadTest test2 = new ThreadTest(20);

test1.start();

test2.start();

}

}

/**

* 此线程有两个ThreadLocal变量,但是由于ThreadLocal是延迟初始的,

* 所以在debug时可以看到线程名为“线程20”的线程的ThreadLocalMap中没有thLcal2这个entry

* @author mxdba

*

*/

class ThreadTest extends Thread {

public static ThreadLocal<Integer> thLocal = new ThreadLocal<Integer>();

public static ThreadLocal<String> thLocal2 = new ThreadLocal<String>();

public Integer num;

public ThreadTest(Integer num) {

super("线程" + num);

this.num = num;

}

@Override

public void run() {

Integer n = thLocal.get();

if(num != 20) {

String s = thLocal2.get();

}

if(n == null) {

thLocal.set(num);

}

System.out.println(thLocal.get());

}

}

接下来分析一下源码,就更加清楚了

关键方法代码


/**

* 关键方法,返回当前Thread的ThreadLocalMap

* [[[每个Thread返回各自的ThreadLocalMap,所以各个线程中的ThreadLocal均为独立的]]]

*/

ThreadLocalMap getMap(Thread t) {

return t.threadLocals;

}

Threadlocal的get方法代码


public T get() {

Thread t = Thread.currentThread();

/**

* 得到当前线程的ThreadLocalMap

*/

ThreadLocalMap map = getMap(t);

if (map != null) {

/**

* 在此线程的ThreadLocalMap中查找key为当前ThreadLocal对象的entry

*/

ThreadLocalMap.Entry e = map.getEntry(this);

if (e != null)

return (T)e.value;

}

return setInitialValue();

}

初始化方法代码


private T setInitialValue() {

/**

* 默认返回null,这个方法为protected可以继承

*/

T value = initialValue();

Thread t = Thread.currentThread();

ThreadLocalMap map = getMap(t);

if (map != null)

map.set(this, value);

else

/**

* 初次创建

*/

createMap(t, value);

return value;

}

Java代码


/**

* 给当前thread初始ThreadlocalMap

*/

void createMap(Thread t, T firstValue) {

t.threadLocals = new ThreadLocalMap(this, firstValue);

}

通过上边的分析,我们发现,ThreadLocal类的使用虽然是用来解决多线程的问题的,但是还是有很明显的针对性
1.最明显的,ThreadLoacl变量的活动范围为某线程,并且我的理解是该线程“专有的,独自霸占”,对该变量的所有操作均有该线程完成!也就是说,ThreadLocal不是用来解决共享,竞争问题的。典型的应用莫过于Spring,Hibernate等框架中对于多线程的处理了

Java代码


private static final ThreadLocal threadSession = new ThreadLocal();

public static Session getSession() throws InfrastructureException {

Session s = (Session) threadSession.get();

try {

if (s == null) {

s = getSessionFactory().openSession();

threadSession.set(s);

}

} catch (HibernateException ex) {

throw new InfrastructureException(ex);

}

return s;

}

这段代码,每个线程有自己的ThreadLocalMap,每个ThreadLocalMap中根据需要初始加载threadSession,这样的好处就是介于singleton与prototype之间,应用singleton无法解决线程,应用prototype开销又太大,有了ThreadLocal之后就好了,对于需要线程“霸占”的变量用ThreadLocal,而该类实例的方法均可以共享。

2.关于内存泄漏:
虽然ThreadLocalMap已经使用了weakReference,但是还是建议能够显示的使用remove方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: