首页 > 其他 > 详细

ThreadLocal类

时间:2021-01-27 22:38:47      阅读:25      评论:0      收藏:0      [点我收藏+]

1.作用

ThreadLocal用于给线程提供一个独立访问的容器。

2.ThreadLocalMap

ThreadLocalMap是ThreadLocal类中的一个静态内部类,其构造方法如下:

 1 /**
 2          * Construct a new map initially containing (firstKey, firstValue).
 3          * ThreadLocalMaps are constructed lazily, so we only create
 4          * one when we have at least one entry to put in it.
 5          */
 6         ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
 7             table = new Entry[INITIAL_CAPACITY];
 8             int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
 9             table[i] = new Entry(firstKey, firstValue);
10             size = 1;
11             setThreshold(INITIAL_CAPACITY);
12         }

每个Thread类对象中都有一个ThreadLocalMap变量引用,

Thread.java
//其他代码

/* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
//其他代码

 

当线程首次调用ThreadLocal对象的set()方法时会新建一个ThreadLocalMap对象,这个ThreadLocalMap对象就是一个有线程隔离特性的容器(废话)。这个容器可以被认为是一个存储(K,V)的map容器。

ThreadLocal.java
//其他代码

/**
     * Create the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the map
     */
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

//其他代码

 

容器内部维护了一个Entry数组,每个Entry对象可以视为一个键值对(K,V),key就是ThreadLocal对象,value就是线程独享的变量,需要注意的是Entry对T和read Local对象封装了弱引用。当ThreadLocal对象的强引用指向Null

后,进行垃圾回收时,ThreadLocal对象会被回收,弱引用不能阻止垃圾回收。

 1 /**
 2          * The table, resized as necessary.
 3          * table.length MUST always be a power of two.
 4          */
 5         private Entry[] table;
 6 
 7 
 8 /**
 9          * The entries in this hash map extend WeakReference, using
10          * its main ref field as the key (which is always a
11          * ThreadLocal object).  Note that null keys (i.e. entry.get()
12          * == null) mean that the key is no longer referenced, so the
13          * entry can be expunged from table.  Such entries are referred to
14          * as "stale entries" in the code that follows.
15          */
16         static class Entry extends WeakReference<ThreadLocal<?>> {
17             /** The value associated with this ThreadLocal. */
18             Object value;
19 
20             Entry(ThreadLocal<?> k, Object v) {
21                 super(k);
22                 value = v;
23             }
24         }

 

ThreadLocal对象的set()方法:

 1  /**
 2      * Sets the current thread‘s copy of this thread-local variable
 3      * to the specified value.  Most subclasses will have no need to
 4      * override this method, relying solely on the {@link #initialValue}
 5      * method to set the values of thread-locals.
 6      *
 7      * @param value the value to be stored in the current thread‘s copy of
 8      *        this thread-local.
 9      */
10     public void set(T value) {
11         Thread t = Thread.currentThread();
12         ThreadLocalMap map = getMap(t);
13         if (map != null) {
14             map.set(this, value);
15         } else {
16             createMap(t, value);
17         }
18     }

 

ThreadLocalMap对象的set()方法:

 1 /**
 2          * Set the value associated with key.
 3          *
 4          * @param key the thread local object
 5          * @param value the value to be set
 6          */
 7         private void set(ThreadLocal<?> key, Object value) {
 8 
 9             // We don‘t use a fast path as with get() because it is at
10             // least as common to use set() to create new entries as
11             // it is to replace existing ones, in which case, a fast
12             // path would fail more often than not.
13 
14             Entry[] tab = table;
15             int len = tab.length;
16             int i = key.threadLocalHashCode & (len-1);
17 
18             for (Entry e = tab[i];
19                  e != null;
20                  e = tab[i = nextIndex(i, len)]) {
21                 ThreadLocal<?> k = e.get();
22 
23                 if (k == key) {
24                     e.value = value;
25                     return;
26                 }
27 
28                 if (k == null) {
29                   //key == null,但是存在值(因为此处的e != null),说明之前的ThreadLocal对象已经被回收了
30                     replaceStaleEntry(key, value, i);
31                     return;
32                 }
33             }
34 
35             tab[i] = new Entry(key, value);
36             int sz = ++size;
37            //cleanSomeSlots(i, sz) 方法用于清除key为Null的entry
38             if (!cleanSomeSlots(i, sz) && sz >= threshold)
39                 rehash();
40         }    

 

 

ThreadLocal防止内存泄漏的措施:

1)将Entry对ThreadLocal对象的引用定义为弱引用,一旦该对象没有强引用,垃圾回收的时候就会被回收,避免了ThreadLocal对象导致的内存泄露;

2)在get()、set()的时候,会排查key为Null的entry,并将这些entry清空;

3)另外,ThreadLocal还提供了remove(ThreadLocal key)方法,当确定当前ThreadLocal用不到了以后,可以及时的清除对key的弱引用和对value的强引用;

4)线程关闭后,容器会自动关闭。

内存泄漏的特例:

虽然有一系列措施,但是,如果:

1)容器中的value比较大;

2)当前线程一直不关闭(例如线程池),线程一直保持着对entry中value的强引用,如下图所示;

技术分享图片

3)一直不set()或者get().

那么还是会内存泄漏。

 

ThreadLocal类

原文:https://www.cnblogs.com/A-PP-Z-droplet/p/14336912.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!