Java并发之ThreadLocal
来源:互联网 发布:怎么读java源码 编辑:程序博客网 时间:2024/05/17 21:54
在Java中,对象是线程共享的,当我们想让线程独自拥有专属于自己的变量时,可以使用ThreadLocal类。
ThreadLocal类提供了线程局部(Thread-Locak)变量。这些变量不同于他们的普通对应物,因为访问某个变量的每个线程都有自己的局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本。所以,每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。
从线程的角度看,目标变量就是线程的本地变量,这也是Local所代表的含义。
一、使用方法
- public T get():返回此线程局部变量的当前线程副本中的值。如果变量没有用于当前线程的值,则先将其初始化为调用 initalValue() 方法返回的值。
- protected T initialValue():返回此线程局部变量的当前线程的”初始值“。线程第一次使用 get() 方法会调用此方法。但如果线程之前调用了 set() 方法,则不会对该线程再调用 initialValue() 方法。通常,此方法对每个线程最多调用一次,但如果在调用 get() 后又使用 remove() 方法,则可能再次调用此方法。
- public void remove():移除此线程局部变量当前线程的值。如果此线程局部变量随后被当前线程读取,且这期间线程没有设置其值,则将调用其 initialValue() 方法重新初始化其值。这将导致在当前线程多次调用 initialValue() 方法。
- public void set(T value):将此线程局部变量的当前线程副本中的值设置为指定值。大部分子类不需要重写此方法,它们只依靠 initialValue() 方法来设置线程局部变量的值。
二、ThreadLocal实现原理
ThreadLocal.ThreadLocalMap threadLocals = null; //Thread对象中含有字段 ThreadLocalMap
get方法
get方法返回当前线程中,存有的ThreadLocal局部变量。如果当前线程中没有存入该值,将调用initialValue方法返回的值。源码如下:
public T get() { Thread t = Thread.currentThread();//当前线程 ThreadLocalMap map = getMap(t);//获取该线程的ThreadLocalMap if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this);//获取map中键为该ThreadLocal的entry if (e != null) {//若有值 则返回该值 @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue();//若没有该值 则调用initialValue方法 }
getMap(Thread t) 方法用来获取线程中的ThreadLocalMap:
ThreadLocalMap getMap(Thread t) { return t.threadLocals;//获取线程t的局部变量表 }
setInitialValue()方法将获取初始值,并往线程的局部变量表中填入threadLocal-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; }
set方法
set方法将往线程的局部变量表中填入变量副本的键值对。
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t);//获取局部变量表 if (map != null) map.set(this, value); else createMap(t, value); }
remove方法
remove方法将清除线程局部变量表中的该threadLocal键值对。
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }
以上几个方法的核心都是对线程的ThreadLocalMap进行操作,下面我们将查看ThreadLocalMap的源码。
三、ThreadLocalMap实现原理
static class ThreadLocalMap { //map中的每个节点Entry,其键key是ThreadLocal并且还是弱引用,这也导致了后续会产生内存泄漏问题的原因。 static class Entry extends WeakReference<ThreadLocal<?>> { Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } /** * 初始化容量为16,以为对其扩充也必须是2的指数 */ private static final int INITIAL_CAPACITY = 16; /** * 真正用于存储线程的每个ThreadLocal的数组,将ThreadLocal和其对应的值包装为一个Entry。 */ private Entry[] table; ///....其他的方法和操作都和map的类似}
总之,为不同线程创建不同的ThreadLocalMap,用线程本身为区分点,每个线程之间其实没有任何的联系,说是说存放了变量的副本,其实可以理解为为每个线程单独new了一个对象。
四、内存泄漏问题
网上大家讨论说ThreadLocal会导致内存泄漏,原因如下:
- 首先ThreadLocal实例被线程的ThreadLocalMap实例持有,也可以看成被线程持有。
- 如果应用使用了线程池,那么之前的线程实例处理完之后出于复用的目的依然存活
- 所以,ThreadLocal设定的值被持有,导致内存泄露。
- java并发之ThreadLocal
- Java并发之ThreadLocal
- java并发之ThreadLocal
- JAVA并发学习之ThreadLocal
- java多线程并发控制之ThreadLocal
- Java并发编程之ThreadLocal类详解
- Java并发编程实践之ThreadLocal变量
- java多线程并发控制之ThreadLocal
- Java并发之ThreadLocal和InheritableThreadLoacal
- JAVA并发--ThreadLocal学习之路
- Java并发编程之ThreadLocal详解
- 并发编程之ThreadLocal
- 并发编程之ThreadLocal
- 并发之ThreadLocal
- Java并发 : ThreadLocal
- Java并发编程-ThreadLocal
- Java并发 ThreadLocal
- java并发编程---ThreadLocal
- @responseBody注解的使用
- TCP/IP协议栈初始化流程
- 设计模式之适配器模式
- C++ 二叉树创建、遍历访问、删除
- SwipeRefreshLayout使用
- Java并发之ThreadLocal
- 函数对象简单使用
- 组合与继承
- leetcode-26
- 同一网段
- CSDN 发布保存失败,请稍后重试。
- GalaxyOJ-725 (二分+容斥)
- D
- 【代码笔记】XML深入学习:DTD约束与DTD语法(1)