Java并发机制(4)--ThreadLocal线程本地变量(转)
个人理解:
说明:看了博客园中大神写的ThreadLocal的详解,感觉还是有些迷糊,下面用自己的理解简单描述下ThreadLocal的机制(难免有误);
1、首先ThreadLocal用于存储对应线程的本地变量,放在哪里呢?每一个线程维护一个threadlocals(这个threadlocals我认为是由ThreadLcoal创建的,是当前线程上的属性,多个ThreadLocal只创建一个:通过ThreadLocal中的createMap方法: t.threadLocals = new ThreadLocalMap(this, firstValue); ),它事实上就是一个ThreadLocalMap,是ThreadLocal的内部类,可以理解为一张Map表,其中包含了Entry键值对,故线程的本地变量就放在这个Entry中:Entry:<ThreadLocal,value>形式,键值对里的ThreadLocal我认为是你用的时候new出来的ThreadLocal,而不是每个线程所维护的那一个ThreadLoca。(不是很确定,但是想到一个线程要放多个变量的话,每个变量new一个ThreadLocal,这些ThreadLocal做为key放在线程所维护的那个ThreadLocalMap中?)。
2、由1中知道,一个ThreadLocal只能存放一个线程的本地变量,所以要存放第二个线程的本地变量,就应该在new ThreadLocal进行存储,所以 private static final ThreadLocal <T> t1=new Threadlocal<>() 这句话可以写在工具类中,且作为静态成员变量使用。
这里我一开始理解错误:把ThreadLocalMap理解成类似于HashMaple了,然后ThreadLocal保存了所有线程的本地变量,这样一个ThreadLocal就可以了?那么多线程访问一个ThreadLocal(共享)依然需要加锁,失去了原来的意义。
3、ThreadLocal的方法理解(源码分析):
3.1、threadlocal.get()方法:
step1:根据当前线程t获取对应的TheradLocalMap;
step2:如果ThreadLocalMap!=null;获取内部的Entry,返回val,结束;
如果ThreadLocalMap==null,调用setInitialValue()方法设置初始值;
step3:setInitialValue()方法判断是否有ThreadLocalMap,(没有则创建),
并调用initialValue()方法获得初始value,这个value=null;
所以,new 出来的Threadlocal要先set(value),然后get(),否则get到null;
或者也可以重写initialValue方法(默认返回一个初始值);
3.2、threadlocal.set()方法:赋值,不多描述。
4、弱引用问题:ThreadLocalMap使用ThreadLocal的弱引用作为key,,如果一个ThreadLocal没有外部强引用引用他,那么系统gc的时候,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,无法访问到这些value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value,永远无法回收,造成内存泄露。
JDK建议将ThreadLocal变量定义成private static的,这样的话ThreadLocal的生命周期就更长,并主动调用remove函数释放。
总结如下:
首先,要区分threadlocals、ThreadLocalMap、ThreadLocal这三者。
其次,ThreadLcoal的主要机制如下。
(1)每个线程Thread内部有一个threadlocals属性,它是ThreadLocal.ThreadLocalMap类型的,内部的Entry<ThreadLocal,value>用来保存变量的副本,只供线程自己使用。
(2)最开始,threadlocals是空的,通过Threadlocal的get或者set方法可以初始化threadlocals(调用createMap方法,源代码在1种),然后你每存一个变量副本,需要new ThreadLocal()出来,存几个new几个,threadlocals以该ThreadLocal为key,变量副本为value进行保存。
(2)我们可以通过ThreadLocal.get()获取对应的ThreadLocal的value。通过ThreadLcoal.set(value)来设置更改threadlocals中的值。
参考资料:
1、博客园-海子-http://www.cnblogs.com/dolphin0520/p/3920407.html
2、http://www.iteye.com/topic/103804
3、http://www.cnblogs.com/xzwblog/p/7227509.html
以下给出两段实例代码,也来自上述参考资料:
1、hibernate中典型的ThreadLocal的应用,获取session;
... 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; }
2、
public class Test { ThreadLocal<Long> longLocal = new ThreadLocal<Long>(); ThreadLocal<String> stringLocal = new ThreadLocal<String>(); public void set() { longLocal.set(Thread.currentThread().getId());
//这里先获取每一个线程thread的ThreadLocalMap,然后map.set(this, value),this就是当前的ThreadLocal,value就是getId的值,也就为每个线程创建完副本,并存放入线程的ThreadLocalMap中,从而实现了线程本地变量的副本创建。
stringLocal.set(Thread.currentThread().getName()); } public long getLong() { return longLocal.get(); } public String getString() { return stringLocal.get(); } public static void main(String[] args) throws InterruptedException { final Test test = new Test(); test.set(); System.out.println(test.getLong()); System.out.println(test.getString()); Thread thread1 = new Thread(){ public void run() { test.set(); System.out.println(test.getLong()); System.out.println(test.getString()); }; }; thread1.start(); thread1.join(); System.out.println(test.getLong()); System.out.println(test.getString()); } }
------------------------------------------------------------------------------------------终-----------------------------------------------------------------------------------------------------------------------------------------------
详情移步:博客园-海子-http://www.cnblogs.com/dolphin0520/p/3920407.html