文章目录
1、简介
先看一张图,下面中每个资源1都是不对其他线程可见的,都是独属于各自的线程

出现的场景:
- 每一个线程都可能产生一个全局变量,并且值都是专属于这一个线程的,别的线程不能使用,供线程调用后面的方法取出使用,避免重复传参数,当然你说我就专门当方法参数传,那也可以不用
- 如果涉及到不安全的工具类的使用,可能会造成问题,比如simpleDateFormat
- 剩下的场景就自己搜索吧
2、之后看一个代码示例,
static ThreadLocal<String> localVar = new ThreadLocal<>();
static void print(String str) {
//打印当前线程中本地内存中本地变量的值
System.out.println(str + " :" + localVar.get());
//清除本地内存中的本地变量
localVar.remove();
}
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
//设置线程1中本地变量的值
localVar.set("线程1中存储在ThreadLocal中的字符串");
//调用打印方法
print("thread1");
//打印本地变量
System.out.println("after remove : " + localVar.get());
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
//设置线程1中本地变量的值
localVar.set("线程2中存储在ThreadLocal中的字符串");
//调用打印方法
print("thread2");
//打印本地变量
System.out.println("after remove : " + localVar.get());
}
});
t1.start();
t2.start();
}
下面的是输出结果:下面只是一种输出结果,thread2和thread1的输出顺序不固定

3、ThreadLocal在java中的是存储结构
(1)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);
}
/**
* 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;
}
/**
* 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);
}
/**
* The table, resized as necessary.
* table.length MUST always be a power of two.
*/
private Entry[] table;
/**
* Construct a new map initially containing (firstKey, firstValue).
* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
下面是Thread.java
/**
* Returns a reference to the currently executing thread object.
*
* @return the currently executing thread.
*/
public static native Thread currentThread();
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
看上面两段代码就知道了,实际上是创建了一个ThreadLocalMap放到了ThreadLocal.ThreadLocalMap threadLocals 中,
而ThreadLocalMap里面是用一个Entry[] table数组当前线程的局部变量,就像下面的关系图一样
Thread,ThreadLoacl,ThreadeLocalMap三个类之间的关系

(2)get方法
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
//ThreadLocalMap.Entry相当于table数组中的某个值
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
private Entry getEntry(ThreadLocal<?> key) {
//通过threadLocalHashCode和数组长度找出此线程在table数组中
//哪个索引位置(i)存储的数据
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
4、为什么ThreadLocalMap中要用数组存储?
1、一个Thread只有一个ThreadLocals(通过看Thread的源码知道)
2、所以同一个线程得到的ThreadLocalMap都是同一个对象(通过getMap方法知道)
3、如果同一个线程创建了多个ThreadLocal,也都是存储在同一个ThreadLocalMap中,所以ThreadLocalMap中需要用Entiy数组来存储
一个线程多ThreadLocal代码示例
static ThreadLocal<String> localVar1 = new ThreadLocal<>();
static ThreadLocal<String> localVar2 = new ThreadLocal<>();
static void print(String str) throws InterruptedException {
//打印当前线程中本地内存中本地变量的值
System.out.println(str + " :" + localVar1.get());
System.out.println(str + " :" + localVar2.get());
//清除本地内存中的本地变量
localVar1.remove();
localVar2.remove();
}
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
localVar1.remove();
localVar2.remove();
//设置线程1中本地变量的值
localVar1.set("线程1中存储在ThreadLocal1中的字符串");
localVar2.set("线程1中存储在ThreadLocal2中的字符串");
//调用打印方法
print("thread1");
//打印本地变量
System.out.println("after remove : " + localVar1.get());
}
});
t1.start();
}
debug图

所以两个ThreadLocal的数据都是存储在同一个ThreadLocalMap中,只是在table中的索引位置不同
下面是两个ThreadLocal如何获得各自在ThreadLocalMap中数组的存储的数据的索引


因为ThreadLocal的threadLocalHashCode都不一样,这样就可以得到各自的数据









