
Java理解ThreadLocal
摘要:
ThreadLocal,也称为线程局部变量,是Java中的一种特殊的线程绑定机制. 它用于确保不同线程之间的变量隔离,以便每个线程可以处理自己的状态. 此外,本文以ThreadLocal类的源代码为切入点,深入分析了ThreadLocal类的工作原理,并给出了应用场景和一般使用步骤.
一个. 了解ThreadLocal
1). ThreadLocal概述
ThreadLocal,也称为线程局部变量,是Java中的一种特殊的线程绑定机制. 它可以为使用该变量的每个线程提供变量值的副本,并且每个线程可以独立更改自身. ,不与其他线程的副本冲突. 通过ThreadLocal访问的数据始终与当前线程相关,也就是说,JVM为每个正在运行的线程绑定了一个私有的本地实例访问空间,从而为多线程环境中经常发生的并发访问问题提供了一种方法. 一种隔离机制.
2). JDK中的ThreadLocal定义
ThreadLocal
此类提供线程局部变量. 这些变量与普通变量(副本)的不同之处在于,每个访问一个线程(通过其get或set方法)的线程都有其自己的,独立初始化的变量副本. ThreadLocal实例通常是希望将状态与线程关联的类中的私有静态字段(例如,用户ID或交易ID).
只要线程是活动的并且ThreadLocal实例是可访问的,则每个线程都对其线程局部变量的副本(参见下图)隐式引用. 线程消失后,其线程本地实例的所有副本都将进行垃圾回收(除非存在对这些副本的其他引用).


我们可以从中提取三个要点:
每个线程都有一个有关ThreadLocal变量的私有值
每个线程都有一个独立于其他线程的上下文来保存此变量的值,并且其他线程不可见.
与变量的初始值无关
可以为ThreadLocal赋予一个初始值,以便每个线程都可以获取该初始值的副本,并且其他线程看不到每个线程对该值的修改.
状态与某个线程相关联
ThreadLocal不是用来解决共享变量的问题,也不是用来协调线程同步的,而是引入了一种机制来促进每个线程处理自己的状态. 理解这一点对于正确使用ThreadLocal至关重要.
3). 应用场景
ThreadLocal类的主要解决方案是将每个线程与其自己的值绑定,以方便其对自己状态的处理. 从视觉上讲,可以将ThreadLocal变量与用于全局存储数据的框进行比较,并且可以将每个线程的私有数据存储在框中. 例如,以下类用于生成每个线程唯一的本地标识符. 线程ID是在首次调用uniqueNum.get()时分配的,在以后的调用中不会更改.

import java.util.concurrent.atomic.AtomicInteger;
public class UniqueThreadIdGenerator {
private static final AtomicInteger uniqueId = new AtomicInteger(0);
private static final ThreadLocal<Integer> uniqueNum = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return uniqueId.getAndIncrement();
}
};
public static void main(String[] args) {
Thread[] threads = new Thread[5];
for (int i = 0; i < 5; i++) {
String name = "Thread-" + i;
threads[i] = new Thread(name){
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ": "
+ uniqueNum.get());
}
};
threads[i].start();
}
System.out.println(Thread.currentThread().getName() + ": "
+ uniqueNum.get());
}
}/* Output(输出结果不唯一):
Thread-1: 2
Thread-0: 0
Thread-2: 3
main: 1
Thread-3: 4
Thread-4: 5
*///:~
两个. ThreadLocal类的深入分析
下面threadlocal使用实例,让我们看一下ThreadLocal的特定实现. 此类提供了四种方法:
public T get() { }
public void set(T value) { }
public void remove() { }
protected T initialValue() { }
其中,get()方法用于获取保存在当前线程中的ThreadLocal变量的值,set()用于设置当前线程中的ThreadLocal变量的值,而remove()是用于删除当前线程中的相关ThreadLocal变量initialValue()是一种受保护的方法,通常需要重写.
1. 原理研究 p>
1). 入口点: get()
首先,让我们先看一下源代码:
/**
* 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); // 获取当前线程的成员变量 threadLocals
if (map != null) {
// 从当前线程的 ThreadLocalMap 获取该 thread-local variable 对应的 entry
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value; // 取得目标值
}
return setInitialValue();
}
2). 关键点: setInitialValue()

/**
* 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(); // 默认实现返回 null
Thread t = Thread.currentThread(); // 获得当前线程
ThreadLocalMap map = getMap(t); // 得到当前线程 ThreadLocalMap类型域 threadLocals
if (map != null)
map.set(this, value); // 该 map 的键是当前 ThreadLocal 对象
else
createMap(t, value);
return value;
}
接下来,我们看一下上述方法中涉及的三种方法: initialValue(),set(this,value)和createMap(t,value).
(1)initialValue()
/**
* Returns the current thread's "initial value" for this
* thread-local variable. This method will be invoked the first
* time a thread accesses the variable with the {@link #get}
* method, unless the thread previously invoked the {@link #set}
* method, in which case the <tt>initialValue</tt> method will not
* be invoked for the thread. Normally, this method is invoked at
* most once per thread, but it may be invoked again in case of
* subsequent invocations of {@link #remove} followed by {@link #get}.
*
* <p>This implementation simply returns <tt>null</tt>; if the
* programmer desires thread-local variables to have an initial
* value other than <tt>null</tt>, <tt>ThreadLocal</tt> must be
* subclassed, and this method overridden. Typically, an
* anonymous inner class will be used.
*
* @return the initial value for this thread-local
*/
protected T initialValue() {
return null; // 默认实现返回 null
}
(2)createMap()
/**
* 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); // this 指代当前 ThreadLocal 变量,为 map 的键
}
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-219713-1.html
听得我分分钟不想看了