ThreadLocal源码分析与使用场景
来源:互联网 发布:网络什么最赚钱的方法 编辑:程序博客网 时间:2024/05/19 01:08
一、概述
ThreadLocal,即线程变量,是一个以ThreadLocal对象为键、任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。可以通过set(T)方法来设置一个值,在当前线程下再通过get()方法获取到原先设置的值。
ThreadLocal实现的思路:Thread类中持有一个ThreadLocalMap的引用,用于存储每一个线程的变量副本,这个map在使用ThreadLocal变量时候被延迟创建和初始化,并在线程退出时候被释放。Map中元素的键为this所指向的ThreadLocal实例(并不是所以为的线程对象),而值对应需要使用的变量的副本。下面上源码
二、源码分析
1. 关于ThreadLocalMap属于Thread还是ThreadLocal之争
publicclass Thread implements Runnable { /* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null; }
以上可以看到Thread类中持有的ThreadLocalMap ();同时,注释中指出了这个变量由ThreadLocal类来维护,下面就是今天的主角ThreadLocal;所以,这个map是被线程所持有的,但是其初始化和维护都是在ThreadLocal中。
2. ThreadLocal中的四个方法
2.1 get方法 及其调用的方法
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue();}/** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @return the map */ThreadLocalMap getMap(Thread t) { return t.threadLocals;}/** * Variant of set() to establish initialValue. Used instead * of set() in case user has overridden the set() method. * * @return the initial value */private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value;}/** * 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 * @param map the map to store. */void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue);}
可以看到,threadLocals最初是从线程t获取。若尚未初始化,则调用 setInitialValue()
2.2 set方法
/** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value);}
这里就可以看到,map的key并非原以为的当前线程对象,而是this,这时候this的指向应该是当前的ThreadLocal对象
2.3 remove方法
/** * Removes the current thread's value for this thread-local * variable. If this thread-local variable is subsequently * {@linkplain #get read} by the current thread, its value will be * reinitialized by invoking its {@link #initialValue} method, * unless its value is {@linkplain #set set} by the current thread * in the interim. This may result in multiple invocations of the * <tt>initialValue</tt> method in the current thread. * * @since 1.5 */ public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }
无F可说
2.3 ThreadLocalMap–一个静态内部类
/** * ThreadLocalMap is a customized hash map suitable only for * maintaining thread local values. No operations are exported * outside of the ThreadLocal class. The class is package private to * allow declaration of fields in class Thread. To help deal with * very large and long-lived usages, the hash table entries use * WeakReferences for keys. However, since reference queues are not * used, stale entries are guaranteed to be removed only when * the table starts running out of space. */static class ThreadLocalMap { /** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. */ static class Entry extends WeakReference<ThreadLocal> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal k, Object v) { super(k); value = v; } } /** * The initial capacity -- MUST be a power of two. */ private static final int INITIAL_CAPACITY = 16; /** * The table, resized as necessary. * table.length MUST always be a power of two. */ private Entry[] table;...
可以看到,ThreadLocalMap是ThreadLocal的一个静态内部类,其中又有一个继承了WeakReference的Entity,是一个定制化的hashMap.
三、show the code
public class LeThreadLocal extends Thread { private static ThreadLocalValue threadLocalValue; ThreadLocal<ThreadLocalValue> threadLocal = new ThreadLocal<>(); public static void main(String[] args) { for (int i = 0; i < 5; i++) { LeThreadLocal t = new LeThreadLocal(); t.start(); } } @Override public void run() { for (int j = 0; j < 5; j++) { getThreadLocalValue().setIndex(getThreadLocalValue().getIndex() + 1); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } getThreadLocalValue().setS(Thread.currentThread().getName()); System.out.println(getThreadLocalValue()); } } public ThreadLocalValue getThreadLocalValue() { if (threadLocal.get() == null) { threadLocal.set(new ThreadLocalValue(null, 0)); getThreadLocalValue(); } return threadLocal.get(); }}class ThreadLocalValue { private String s; private int index; public String getS() { return s; } public void setS(String s) { this.s = s; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public ThreadLocalValue(String s, int index) { this.s = s; this.index = index; } @Override public String toString() { return "ThreadLocalValue{" + "s='" + s + '\'' + ", index=" + index + '}'; }}
测试输出
ThreadLocalValue{s='Thread-4', index=1}ThreadLocalValue{s='Thread-0', index=1}ThreadLocalValue{s='Thread-1', index=1}ThreadLocalValue{s='Thread-2', index=1}ThreadLocalValue{s='Thread-3', index=1}ThreadLocalValue{s='Thread-4', index=2}ThreadLocalValue{s='Thread-0', index=2}ThreadLocalValue{s='Thread-3', index=2}ThreadLocalValue{s='Thread-2', index=2}ThreadLocalValue{s='Thread-1', index=2}ThreadLocalValue{s='Thread-1', index=3}ThreadLocalValue{s='Thread-4', index=3}ThreadLocalValue{s='Thread-0', index=3}ThreadLocalValue{s='Thread-3', index=3}ThreadLocalValue{s='Thread-2', index=3}ThreadLocalValue{s='Thread-1', index=4}ThreadLocalValue{s='Thread-3', index=4}ThreadLocalValue{s='Thread-4', index=4}ThreadLocalValue{s='Thread-0', index=4}ThreadLocalValue{s='Thread-2', index=4}ThreadLocalValue{s='Thread-1', index=5}ThreadLocalValue{s='Thread-0', index=5}ThreadLocalValue{s='Thread-3', index=5}ThreadLocalValue{s='Thread-4', index=5}ThreadLocalValue{s='Thread-2', index=5}
分析: ThreadLocal变量的使用不同于普通变量,对于本例中的静态成员变量而言,应该在每次使用该变量的时候都首先在ThreadLocal中获取这个变量,而不是直接使用其引用进行操作。这也充分印证了,ThreadLocal为每个线程维护了一个副本,而不是多线程操作这个共享的变量。也就是说使用ThreadLocal类去维护一个变量,并不是为了让多线程共同去的效果叠加的操作这个变量,而是互不干涉的使用这个变量的副本,他们的效果不能叠加。
例子中的使用方式是,在可执行(Runnable)的类中维护一个ThreadLocal实例,并在初次使用需要隔离的变量时候,调用threadLocal.set(value)方法,并在后续操作和使用隔离变量value的时候都先在threadLocal中get,而不是直接操作变量的引用。
在Session维护的场景中也经常用到ThreadLocal,其使用方式是将比较宝贵的连接或者会话资源保存在threadLocal中,在这个线程的声明周期内都使用这个session;
易错点:
即便是ThreadLocal中变量,在使用时要先在threadLocal中get,而不是直接操作器引用,尤其是在类静态变量的使用上;要时刻想着去拿副本
在ThreadLocalMap中,key是当前threadLocal的实例,所以一个ThreadLocal只是维护了一个变量,如果有两个变量,那么需要用两个ThreadLocal的实例。
四、关于内存泄漏
(参考来源: http://blog.csdn.net/wudiyong22/article/details/52141608)
ThreadLocal的实现是这样的:每个Thread 维护一个 ThreadLocalMap 映射表,这个映射表的 key 是 ThreadLocal 实例本身,value 是真正需要存储的Object。也就是说 ThreadLocal 本身并不存储值,它只是作为一个 key 来让线程从 ThreadLocalMap 获取 value。值得注意的是图中的虚线,表示ThreadLocalMap 是使用 ThreadLocal 的弱引用作为 Key 的,弱引用的对象在 GC 时会被回收。
ThreadLocal为什么会内存泄漏
ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用来引用它,那么系统 GC 的时候,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永远无法回收,造成内存泄漏。
其实,ThreadLocalMap的设计中已经考虑到这种情况,也加上了一些防护措施:在ThreadLocal的get(),set(),remove()的时候都会清除线程ThreadLocalMap里所有key为null的value。
防止措施:
在每次使用完ThreadLocal,都调用它的remove()方法,清除数据
- ThreadLocal源码分析与使用场景
- ThreadLocal应用场景以及源码分析
- ThreadLocal与WeakReference 源码分析
- ThreadLocal原理与源码分析
- ThreadLocal使用和源码分析
- ThreadLocal与Synchronized的使用场景
- ThreadLocal与Synchronized的使用场景
- ThreadLocal与Synchronized的使用场景
- ThreadLocal与Synchronized的使用场景
- ThreadLocal与Synchronized的使用场景
- ThreadLocal的分析与使用
- ThreadLocal使用场景
- ThreadLocal使用场景
- ThreadLocal使用、场景、原理
- ThreadLocal的用法与源码分析
- ThreadLocal类详解与源码分析
- Threadlocal 源码分析与内存泄漏
- ThreadLocal基本使用和源码分析
- 【JSP】常见问题解决
- date 与timeStmp
- hibernate中的persist() 和 save() 区别
- IntelliJ Idea 2017 免费激活方法
- java中i++和++i的区别
- ThreadLocal源码分析与使用场景
- django 出现的错误
- hping3 使用详解
- Linux中的信号量
- 随机数组
- Python安装Image库
- 请求如何进入ASP.NET MVC框架
- STC89C52中断系统
- 序列化注意事项