1.JAVA多线程(十)Java多线程之ThreadLocal
1.1 ThreadLocal类
ThreadLocal类主要解决的就是让每个线程绑定自己的值,可以将ThreadLocal类形象的比喻成存放数据的盒子,盒子中可以存储每个线程的私有数据。
如果你创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的本地副本,这也是ThreadLocal变量名的由来。他们可以使用 get()和 set())方法来获取默认值或将其值更改为当前线程所存的副本的值,从而避免了线程安全问题。
每个线程往ThreadLocal中读写数据是线程隔离,互相之间不会影响的,由于不需要共享信息,自然就不存在竞争问题了,从而保证了某些情况下线程的安全,以及避免了某些情况需要考虑线程安全必须同步带来的性能损失!
1.2 ThreadLocal示例
package com.yuanxw.chapter10;
import java.util.Random;
/**
* ThreadLocal
* 线程局部变量
*/
public class ThreadLocalExample {
private static ThreadLocal<String> defaultThreadLocal = new ThreadLocal(){
@Override
protected Object initialValue() {
return "==initialValue==";
}
};
private static ThreadLocal<String> threadLocal = new ThreadLocal();
public static void main(String[] args) throws InterruptedException {
System.out.println("获得defaultThreadLocal默认值:"+defaultThreadLocal.get());
// 线程-A
Thread thread1 = new Thread(() -> {
// 设置【threadLocal】对象值为:张三
threadLocal.set("张三");
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(String.format("【%s】线程-执行threadLocal值:【%s】", Thread.currentThread().getName(),threadLocal.get()));
}, "Thread-A");
// 线程-B
Thread thread2 = new Thread(() -> {
// 设置【threadLocal】对象值为:李四
threadLocal.set("李四");
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(String.format("【%s】线程-执行threadLocal值:【%s】", Thread.currentThread().getName(),threadLocal.get()));
}, "Thread-B");
thread1.join();
thread2.join();
thread1.start();
thread2.start();
Thread.sleep(new Random().nextInt(1000));
System.out.println(String.format("【%s】线程-执行threadLocal值:【%s】", Thread.currentThread().getName(),threadLocal.get()));
}
}
执行结果:
获得defaultThreadLocal默认值:==initialValue==
【Thread-B】线程-执行threadLocal值:【李四】
【Thread-A】线程-执行threadLocal值:【张三】
【main】线程-执行threadLocal值:【null】
1.3 ThreadLocal对应的底层结构图
每个 Thread 都有一个 ThreadLocal.ThreadLocalMap 对象。当调用一个 ThreadLocal 的 set(T value) 方法时,先得到当前线程的 ThreadLocalMap 对象,然后将 ThreadLocal->value 键值对插入到该 Map 中,get() 方法类似。
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
/**
* 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);
}
1.3 ThreadLocal 内存泄露问题
在一些场景 (尤其是使用线程池) 下,ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候,key 会被清理掉,而 value 不会被清理掉。这样一来,ThreadLocalMap 中就会出现key为null的Entry。假如我们不做任何措施的话,value 永远无法被GC 回收,这个时候就可能会产生内存泄露。ThreadLocalMap实现中已经考虑了这种情况,在调用 set()、get()、remove() 方法的时候,会清理掉 key 为 null 的记录。使用完 ThreadLocal方法后 最好手动调用remove()方法。
– 以上为《JAVA多线程(十)Java多线程之ThreadLocal》,如有不当之处请指出,我后续逐步完善更正,大家共同提高。谢谢大家对我的关注。
——厚积薄发(yuanxw)
来源:CSDN
作者:在天庭当托塔李天王的日子
链接:https://blog.csdn.net/yuan_xw/article/details/103986624