核心思想:
每个线程存有一个map,这个map里面数组存储entry,是键threadlocal对应的value。意思一个线程对应多个键threadlocal对应多个value;(看源码区分this和thread.currentthread,一个是字节码对象一个是当前线程对象)
核心元素:只有一点
ThreadLocal.ThreadLocalMap
下面三行代码来自Thread
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
一、实例化,且重写T initialValue(),初始化首个变量值;
java.lang.ThreadLocal
public Integer initialValue(){
return 0;
}
};
那重写后,哪里调用?当首次get的时候会调用。
先获取当前线程,把当前线程里面的变量ThreadLocalMap 拿出来,
看看是否为空,不为空,就直接获取this(threadlocal字节码对象)对应的值;
空,即把这个Thread里面的变量ThreadLocalMap初始化, new一个数组(map思想,开放寻址);并把楼上初始化值设置进去(索引就是哈希位与数组长度);
put也就一样的了;核心就是每个线程Thread里面的ThreadLocalMap 变量;
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
private Entry getEntry(ThreadLocal key) {
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);
}
private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal k = e.get();
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
获取过程楼上代码一层一层跟下去,没什么特别。为了解决内存溢出问题,jdk大神,把localThread弱引用,原因,如果不是弱引用,就算threadlocal自己null短了引用。但是Thread对threadlocalmap的引用还是在,因为不仅仅你一个threadlocal,接着map里面还是存在。故弱引用。gc回收了treadlocal。key就变成了null。大神map在put、get等操作如果发现null就把脏数据给remove了。这个是亮点。有时间要研究下。而且你把keynull的去掉。那后面的用不用重新hash呢。因为之前这里不为空,我把开放寻址的加1放到后面去了。他是不是要挪上来。麻烦
private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;
int len = tab.length;
// expunge entry at staleSlot
tab[staleSlot].value = null;
tab[staleSlot] = null;
size--;
// Rehash until we encounter null
Entry e;
int i;
for (i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal k = e.get();
if (k == null) {
e.value = null;
tab[i] = null;
size--;
} else {
int h = k.threadLocalHashCode & (len - 1);
if (h != i) {
tab[i] = null;
// Unlike Knuth 6.4 Algorithm R, we must scan until
// null because multiple entries could have been stale.
while (tab[h] != null)
h = nextIndex(h, len);
tab[h] = e;
}
}
}
return i;
}
实例:
多个线程公用一个对象(区域),想拥有同个变量的多个副本。且各个线程一个副本;
public static void main(String[] args) {
test.A a = new test().new A();
new Thread(new test().new Count("1",a)).start();
new Thread(new test().new Count("2",a)).start();
}
public class A{
java.lang.ThreadLocal
public Integer initialValue(){
return 0;
}
};
public void doSomething(){
ad.set(ad.get()+1);
}
}
public class Count implements java.lang.Runnable{
String threadName;
A a;
public Count(String threadName,A a){
this.threadName = threadName;
this.a = a;
}
@Override
public void run() {
while(true){
a.doSomething();
System.out.println(this.threadName+" "+a.ad.get());
}
}
}