文章目录
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
都不一样,这样就可以得到各自的数据