Java基础知识_ThreadLocal
一、什么是ThreadLocal
首先看一下JDK文档介绍
1 /** 2 * This class provides thread-local variables. These variables differ from 3 * their normal counterparts in that each thread that accesses one (via its 4 * {@code get} or {@code set} method) has its own, independently initialized 5 * copy of the variable. {@code ThreadLocal} instances are typically private 6 * static fields in classes that wish to associate state with a thread (e.g., 7 * a user ID or Transaction ID). 8 * 9 * <p>For example, the class below generates unique identifiers local to each 10 * thread. 11 * A thread's id is assigned the first time it invokes {@code ThreadId.get()} 12 * and remains unchanged on subsequent calls. 13 */
总结一下,ThreadLocal提供了线程的局部变量,每个线程都可以通过set()和get()来对局部变量进行操作,但不会和其他线程的局部变量进行冲突,实现了线程的数据隔离。
简要言之,往ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的
二、为什么要学习ThreadLocal?
从上面可以得出:ThreadLocal可以让我们拥有当前线程的变量,那这个作用有什么用呢???
2.1管理Connection
最典型的管理数据库的Connection:当时在学JDBC的时候,为了方便操作写了一个简单的数据库连接池,需要数据库连接池的理由也很简单,频繁创建和关闭Connection是一件非常耗费资源的操作,因此需要创建数据库连接池。
那么,数据库连接池怎么管理呢??我们交给ThreadLocal来进行管理,为什么交给它来管理呢??ThreadLocal能够实现当前线程的操作都是用同一个Connection保证了事务。
2.2 避免一些参数传递
避免一些参数的传递和理解可以参考一下Cookie和Session
每当我们访问一个页面的时候,浏览器都会帮我们从硬盘中找到对应的Cookie发过去
浏览器就很聪明,不会发送别的网站Cookie过去,只带当前网站发布过来的Cookie过去。
浏览器就相当于我们的ThreadLocal,它仅仅会发送我们当前浏览器存在的Cookie(ThreadLocal的局部变量),不同的浏览器对Cookie是隔离的,同样的:线程之间的ThreadLocal变量也是隔离的。
如果用了ThreadLocal的话,ThreadLocal就相当于一个机构,要用的时候跟他取就行了
三、ThreadLocal实现的源码
想要更好的去理解ThreadLocal,那就得翻翻它是怎么实现的了。
首先我们来康康ThreadLocal的set()方法,因为我们一般都是使用new完对象,就往里面set对象了。
1 public void set(T value) { 2 Thread t = Thread.currentThread(); 3 ThreadLocalMap map = getMap(t); 4 if (map != null) 5 map.set(this, value); 6 else 7 createMap(t, value); 8 }
上面有个ThreadLocalMap,我们去看看这是什么?
1 static class ThreadLocalMap { 2 3 /** 4 * The entries in this hash map extend WeakReference, using 5 * its main ref field as the key (which is always a 6 * ThreadLocal object). Note that null keys (i.e. entry.get() 7 * == null) mean that the key is no longer referenced, so the 8 * entry can be expunged from table. Such entries are referred to 9 * as "stale entries" in the code that follows. 10 */ 11 static class Entry extends WeakReference<ThreadLocal<?>> { 12 /** The value associated with this ThreadLocal. */ 13 Object value; 14 15 Entry(ThreadLocal<?> k, Object v) { 16 super(k); 17 value = v; 18 } 19 } 20 //....很长 21 }
通过上面我们可以发现的是ThreadLocalMap是ThreadLocal的一个内部类。用Entry类进行存储。
我们的值都是存储到这个map上的,key是当前ThreadLocal对象
如果该Map不存在,则初始化一个:
1 void createMap(Thread t, T firstValue) { 2 t.threadLocals = new ThreadLocalMap(this, firstValue); 3 }
如果Map不存在,则从Thread中获取
1 /** 2 * Get the map associated with a ThreadLocal. Overridden in 3 * InheritableThreadLocal. 4 * 5 * @param t the current thread 6 * @return the map 7 */ 8 ThreadLocalMap getMap(Thread t) { 9 return t.threadLocals; 10 }
Thread维护了ThreadLocalMap变量
1 /* ThreadLocal values pertaining to this thread. This map is maintained 2 * by the ThreadLocal class. */ 3 ThreadLocal.ThreadLocalMap threadLocals = null
从上面可以看出ThreadLocalMap是在ThreadLocal中使用内部类来编写的,但对象的引用是在Thread中。
于是我们可以总结出:Thread为每个线程维护了ThreadLocalMap这么一个Map,而ThreadLocalMap的key是LocalThread对象本身,value则是要存储的对象。
有了上述基础,我们看get方法就一点都不难理解了。
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 }
3.1 ThreadLocal原理总结
1、每个Thread维护着一个ThreadLocalMap的引用
2、ThreadLocalMap是ThreadLocal的内部类,用Entry来存储
3、调用ThreadLocal的set()方法时,实际上就是往ThreadLocalMap设置值,key是ThreadLocal对象,值是传递进来的对象。
4、调用ThreadLocal的get()方法时,实际上就是往ThreadLocalMap获取值,key是ThreadLocal对象。
5、ThreadLocal本身并不储存值,它只是作为一个key来让线程从ThreadLocalMap获取value
正因为这个原理,所以ThreadLocal能够实现数据隔离,获取当前线程的局部变量值,不受其他线程影响。
四、避免内存泄漏
我们来看一下ThreadLocal的对象引用图

ThreadLocal内存泄漏的根源是:由于ThreadLocalMap的生命周期和Thread一样长,如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用。
想要避免内存泄漏,就要手动remove掉
五、总结
ThreadLocal设计的目的就是为了能够在当前线程中有属于自己的变量,并不是为了解决并发或者共享变量的问题

浙公网安备 33010602011771号