ThreadLocal简析

简介

  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

相关文章
相关标签/搜索