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设计的目的就是为了能够在当前线程中有属于自己的变量,并不是为了解决并发或者共享变量的问题

 

posted @ 2019-09-10 21:20  chyblogs  阅读(182)  评论(0)    收藏  举报