背景
ThreadLocal提供了一种解决并发问题的新思路,以往为了多线程访问全局变量(共享变量)不出现安全问题,我们可以通过同步监视器来实现,而ThreadLocal是把共享变量变为线程独自拥有的变量,这样就不存在共享变量了,自然也不会有并发共享变量的问题。
栗子
先整一个栗子,方便下面对源码的理解
import java.util.Random;
public class Demo2 {
private static ThreadLocal data = new ThreadLocal();
static class InnerRunnable implements Runnable {
@Override
public void run() {
int num = new Random().nextInt();
data.set(num);
System.out.println(Thread.currentThread().getName() + " " + num);
System.out.println(Thread.currentThread().getName() + " " + data.get());
}
}
public static void main(String[] args) {
InnerRunnable innerRunnable = new InnerRunnable();
Thread thread = new Thread(innerRunnable);
Thread thread1 = new Thread(innerRunnable);
thread.start();
thread1.start();
}
}
1、首先我们是需要创建一个全局ThreadLocal变量
ThreadLocal data = new ThreadLocal():
2、调用set方法进行存值操作
int num = new Random().nextInt();
data.set(num);
3、让我们来看一下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);
}
进入到set方法后:
3.1、获取当前线程
Thread t = Thread.currentThread();
3.2、获取线程私有的ThreadLocalMap
ThreadLocalMap map = getMap(t);
为什么说线程私有?
来看getMap(Thread t)具体做了什么
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
getMap(Thread t)会返回线程t的threadLocals
ThreadLocal.ThreadLocalMap threadLocals = null;
可以看到threadLocals是一个ThreadLocalMap,说明每个线程都有自己的一个ThreadLocalMap。
3.4、获取map后
3.4.1 如果map == null
createMap(t, value);
3.4.1.1、createMap做了什么呢?
map为空当然要先创建map对象:
t.threadLocals = new ThreadLocalMap(this, firstValue);
ThreadLocalMap(this, firstValue);构造函数做了什么呢?
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);
}
①、实例化Entry[] 数组,初始长度为INITIAL_CAPACITY(16)
private Entry[] table;
table = new Entry[INITIAL_CAPACITY];
②、获取存value的索引位置
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
我的理解是一个取模动作
③、将value和firstKey存到指定位置,key是ThreadLocal的虚引用
④、设置存储个数size,因为是初始化所以是1
⑤、设置阈值,当到达初始化长度的三分之二的时候,会对数组进行扩容
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
3.4.2 若map不为空
map.set(this, value);
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
如果不为空,首先访问Entry数组,通过ThreadLocal的哈希值取模获得存储位置,获得该位置存储的Entry e = tab[i],判断该位置是否已经存有一个Entry,有的话,判断key是否相等,是的话更新value,如果该位置key是null,重新设置?,如果该位置没有一个Entry,直接存入,this指当前的ThreadLocal;
二、get()