先看下面一段代码
public class App {
private static final ThreadLocal<Integer> tnum = ThreadLocal.withInitial(()->0);
private static Integer num =0;
public static void main(String[] args) {
for(int i=0;i<3;i++){
new Thread(()->{
for(int j=0;j<10;j++){
try {
TimeUnit.MILLISECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
tnum.set(tnum.get()+1);
num++;
}
System.out.println(Thread.currentThread().getName()+"---tnum=="+tnum.get());
System.out.println(Thread.currentThread().getName()+"----num=="+num);
},"THREAD"+i).start();
}
}
}
看下执行结果
THREAD2---tnum==10
THREAD0---tnum==10
THREAD0----num==27
THREAD1---tnum==10
THREAD1----num==27
THREAD2----num==27
不管执行几次,num的值因为线程安全问题一直在变。但是tnum一直都是10,也就是线程循环执行的次数。这也说明了threadLoacl是线程隔离的,每个线程之间相互不影响,那么接着就看下源码
上述代码中实现tnum自增的是tnum.set(tnum.get()+1); 那么就先看下get()方法吧
java.lang.ThreadLocal#get
public T get() {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程的ThreadLocalMap类型的属性threadLocals
ThreadLocal.ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocal.ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
1.)获取当前线程的ThreadLocalMap类型的属性threadLocals 【java.lang.ThreadLocal#getMap】
ThreadLocal.ThreadLocalMap getMap(Thread t) {
//这里说明Thread类有一个ThreadLocalMap类型的属性,下面也附有Thread类的部分截图
return t.threadLocals;
}
2)从ThreadLocalMap中获取内容
private ThreadLocal.ThreadLocalMap.Entry getEntry(ThreadLocal<?> key) {
//把key的hash值和table的length值与操作
int i = key.threadLocalHashCode & (table.length - 1);
//获取对应的值
ThreadLocal.ThreadLocalMap.Entry e = table[i];
/**
* 判断 e.get() == key是因为,与hashMap不同,
* ThreadLocalMap的Entry位置的值冲突之后,不做链表或者树化的操作,而是放到Entry的下一个位置
*/
if (e != null && e.get() == key)
return e;
else
/**
* 这里基本上就是遍历Entry的数组找到key对应的Entry,顺带做一下啊内存泄漏工作的处理
*/
return getEntryAfterMiss(key, i, e);
}
/**
* Entry继承WeakReference,key是弱引用,
* 一次GC之后key就没了,但是value值还有引用,容易造成内存泄漏
* 这也是为什么一般ThreadLocal的变量都是用static final来修饰的
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
3)如果当前线程的threadLocals为空,则执行setInitialValue()初始化
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocal.ThreadLocalMap map = getMap(t);//同样的先获取map
if (map != null)//不为空则直接赋值
map.set(this, value);
else
createMap(t, value);//为空,则创建map并赋值
return value;
}
/**
* set方法
*/
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
ThreadLocal.ThreadLocalMap.Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
//遍历Entry 数组,直到遇到空的,这也说明了,如果槽位值重复了,不做链表,而是放到下一个槽位
for (ThreadLocal.ThreadLocalMap.Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
//Entry的key等于要添加的key值,value就覆盖原来的值
if (k == key) {
e.value = value;
return;
}
//k为空,那就需要处理内存泄漏的问题了
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
//此时tab[i]为空,则创建新的Entry对象并放到数组中去
tab[i] = new ThreadLocal.ThreadLocalMap.Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
整个关系图差不多是这个样子,一个线程对应一个map,但是map的entry的key值是ThreadLocal对象,所有key值都是一样的