ThreadLocal的实现原理就是ThreadLocal里面有一个静态的ThreadLocalMap,这个是怎么操作的呢,往下看
ThreadLocalMap是ThreadLocal里面的一个静态类
源码如下所示
ThreadLocalMap是Thread里面的一个属性
为什么这样说呢,首先我们看到Thread类里面有一个属性就叫做ThreadLocalMap
然后我们看一下ThreadLocal里面的set()方法,看到源码可以看到当当前线程的ThreadLocalMap是null的时候,此时就会走下面的红色框里面的代码
然后就是会走下面的方法,然后new一个ThreadLocalMap赋值给当前线程的threadLocals这个变量,threadLocals这个变量在Thread里面就是ThreadLocal.ThreadLocalMap类型的
然后当set()的时候,会把当前实例化对象的ThreadLocal作为key值,然后把set()里面的参数当成value放到这个ThreadLocalMap里面
在ThreadLocal里面源码set(),此时可以看到set()的源码就是首先获取当前线程,然后利用当前线程获取一个ThreadLocalMap的对象,然后如果ThreadLocalMap对象为空,则把当前ThreadLocal当成key,set()里面的参数当成value放到ThreadLocalMap集合里面,否则创建这个ThreadLocalMap对象,然后把当前ThreadLocal当成key,set()里面的参数当成value放到ThreadLocalMap集合里面
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
上面的createMap(t,value)源码如下
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
看到上面的代码之后,我们也能知道为什么ThreadLocal可以把创建的变量只能被当前线程访问,其他线程则无法访问和修改因为ThreadLocal的值是放入了当前线程的一个ThreadLocalMap实例中,所以只能在本线程中访问,其他线程无法访问。
然后在jdk1.8里面,发现ThreadLocalMap里面是用Entry[]这个数组来实现ThreadLocalMap的功能
使用过程是:Entry类里面的key值就是ThreadLocal,并且是弱引用,然后Entry类里面的value属性对应放入到ThreadLocalMap调用set方法里面的第二个参数
看代码:下面是 jdk1.8里面的ThreadLocalMap类里面的set方法,然后下面的蓝色代码里面的Entry类
private void set(ThreadLocal<?> key, Object 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类就是指ThreadLocalMap类里面的Entry类,如下所示
然后我们看一下ThreadLocalMap里面的set()方法是怎么进行存储的
首先可以看到下面的红色代码里面的threadLocalHashCode就是获取当前ThreadLocal实例对象的threadLocalHashCode属性,这个threadLocalHashCode属性就是唯一的,它是怎么来的呢,看下面的图片,首先是ThreadLocal类里面的HASH_INCREMENT常量,然后通过下面的nextHashCode()方法来进行原子性的增加1,然后把nextHashCode()方法的返回值给当前的ThreadLocal实例化对象的threadLocalHashCode属性,这样就保证唯一性了
然后把这个ThreadLocal的threadLocalHashCode值当成ThreadLocalMap类里面的Entry数组的索引值,就是下面的绿色代码,这样就把ThreadLocal调用的set()方法的两个参数都存储到ThreadLocalMap里面的Entry数组里面了
private void set(ThreadLocal<?> key, Object 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();
}
能看到这里的同学,就帮忙点个赞吧,Thanks♪(・ω・)ノ