ThreadLocal在Java多线程开发中常见的一个类,在面试中也经见的问题,好比ThreadLocal的做用是什么,ThreadLocal的实现原理是什么等等。ThreadLocal是java中一个类,用于实现变量在多线程并发环境下维持线程的封闭性(封闭指的是可变对象对于其余线程是不可访问,不可操做的)。因此ThreadLocal其实能够当作一个线程内的局部变量来理解。java
当咱们建立一个数据库链接,而且不但愿在代码中互相传递,咱们会将它设置成全局变量。然而数据库链接并不必定是线程安全的,咱们但愿每个线程维持一个链接。面试
1 private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() { 2 public Connection initialValue() { 3 return DriverManager.getConnection(DB_URL); 4 } 5 }; 6 7 public static Connection getConnection() { 8 return connectionHolder.get(); 9 }
如上述代码,当不一样的线程来调用getConnection方法的时候,获取到是不同的Connection对象。hibernate的session管理也是经过ThreadLocal来实现的。数据库
先来看一下ThreadLocal中最经常使用的几个方法。安全
1 public T get() { 2 Thread t = Thread.currentThread(); 3 ThreadLocalMap map = getMap(t); 4 if (map != null) { 5 ThreadLocalMap.Entry e = map.getEntry(this); 6 if (e != null) { 7 @SuppressWarnings("unchecked") 8 T result = (T)e.value; 9 return result; 10 } 11 } 12 return setInitialValue(); 13 } 14 15 ThreadLocalMap getMap(Thread t) { 16 return t.threadLocals; 17 } 18 19 public void set(T value) { 20 Thread t = Thread.currentThread(); 21 ThreadLocalMap map = getMap(t); 22 if (map != null) 23 map.set(this, value); 24 else 25 createMap(t, value); 26 }
ThreadLocal的get方法和set方法算是最经常使用的方法了。第3行和第21中对ThreadLocal的操做都是经过一个ThreadLocalMap进行读写来完成get和set,ThreadLocalMap是一个散列表。从第5行和第23行看出,ThreadLocalMap的key是this,指代的是ThreadLocal自己,value则是线程对应的变量值。有一点要注意的是,ThreadlocalMap是在ThreadLocal内部定义的静态内部类,倒是Thread类的属性。session
刚才说过ThreadlocalMap是一个散列表,可是它跟HashMap不同。HashMap解决Hash冲突的方式是使用分离链表法,可是ThreadlocalMap是经过线性探测法来解决Hash冲突,这里就不对这方面进行细讲。多线程
简介了ThreadLocal不顺便讲一讲ThreadLocal内存泄漏问题感受很不专业,可是讲这个问题以前,能够先看一下上面图中,ThreadLocalMap的Entry使用的是一个弱引用(WeakReference),Java的引用分为强软弱虚四种,弱引用的特色是,当一个对象只被一个弱引用连接的时候,下次GC就会将刚对象回收。其余的引用细节这里不作赘述。并发
内存泄漏缘由:当一个ThreadLocal对象使用完毕被系统回收以后,由于Entry中的key为弱引用(这里并不是整个entry),ThreadLocalMap中就有一个Entry中的Key会被设置为Null,此时value仍然保存和entry之间的强连接,致使value没法回收。假如线程一直不关闭,而ThreadLocalMap有大量的key被回收,就会存在一堆entry的key为null,value各不相同且没法回收的情况,因此ThreadLocal的get、set、remove中都会进行对key==null的entry进行清理。源码分析
有人说内存泄漏是由于弱引用引发的,其实并非,若是使用强引用,则会致使必须手动清除不须要使用的entry。使用上更加麻烦,而且更加容易出Bug。this
总的来讲,当ThreadLocal变量已经不使用的时候,最好作一次remove()动做进行清理,这会是一个好习惯。spa