public class ThreadLocal1<T> {
//当创建了一个 ThreadLocal 的实例后,它的散列值就已经确定了,
//threadLocal实例的hashCode是通过nextHashCode()方法实现的,该方法实际上总是用一个AtomicInteger(初始值为0)加上0x61c88647来实现的。
//0x61c88647这个数是有特殊意义的,它能够保证hash表的每个散列桶能够均匀的分布,这是Fibonacci Hashing,
//0x61c88647 这个就比较神奇了,它可以使 hashcode 均匀的分布在大小为 2 的 N 次方的数组里。下面写个程序测试一下:
//也正是能够均匀分布,所以threadLocal选择使用开放地址法来解决hash冲突的问题。
/*public static void main(String[] args) {
AtomicInteger hashCode = new AtomicInteger(); // 一直在增加
int hash_increment = 0x61c88647;
int size = 16;
List <Integer> list = new ArrayList <> ();
for (int i = 0; i < size; i++) {
list.add(hashCode.getAndAdd(hash_increment) & (size - 1));
}
System.out.println("original:" + list);
Collections.sort(list);
System.out.println("sort: " + list);
}
size=16: [7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9, 0] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
size=32 [7, 14, 21, 28, 3, 10, 17, 24, 31, 6, 13, 20, 27, 2, 9, 16, 23, 30, 5, 12, 19, 26, 1, 8, 15, 22, 29, 4, 11, 18, 25, 0]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
*/
private final int threadLocalHashCode = nextHashCode();//对象的属性,不变化的。
private final static int HASH_INCREMENT = 0x61c88647;//1640531527。类的属性。
//类的属性,对象共享,一直在增加。
private static AtomicInteger nextHashCode = new AtomicInteger();// private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);//nextHashCode自己变成了HASH_INCREMENT=1640531527
} protected T initialValue() {//用于重写
return null;
} public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
return new SuppliedThreadLocal<>(supplier);
} public ThreadLocal() {
} //有可能第一次调用get不调用set,map为null,setInitialValue-initialValue赋予map=threadLocals初值。
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);//返回调用get()的线程的threadLocals,是一个ThreadLocalMap,这个ThreadLocalMap是ThreadLocal的内部类。
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();//如果map为空,也就是第一次没有调用set直接get(或者调用过set,又调用了remove)时,为其设定初始值
} private T setInitialValue() {
T value = initialValue();//initialValue方法为第一次调用get方法提供一个初始值。
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
/* ThreadLocal<Integer> x = new ThreadLocal<Integer>();
for (int i = 0; i < 3; i++) {
new Thread(new Runnable() {
public void run() {
x.set(new Random().nextInt());
}
}).start();
}*/
//A,B,C线程通过共享变量ThreadLocal<Integer> x.set(s)。A,B,C线程分别修改的是自己线程的threadLocals=ThreadLocalMap
// 别的线程修改不了这个线程的threadLocals,所以对这个线程的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);
} public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
} ThreadLocalMap getMap(Thread t) {
return t.threadLocals;//返回这个线程的threadLocal属性
}
void createMap(Thread t, T firstValue) {//threadLocals是一个ThreadLocalMap,key是调用ThreadLocal方法的这个ThreadLocal。
//ThreadLocal的方法肯定是ThreadLocal对象在调用。
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
T childValue(T parentValue) {
throw new UnsupportedOperationException();
} static final class SuppliedThreadLocal<T> extends ThreadLocal<T> { private final Supplier<? extends T> supplier;
SuppliedThreadLocal(Supplier<? extends T> supplier) {
this.supplier = Objects.requireNonNull(supplier);
}
@Override
protected T initialValue() {
return supplier.get();
}
} static class ThreadLocalMap1 {//ThreadLocalMap里面有一个数组table,table里面的元素是Entry extends WeakReference。
//WeakReference里面属性有value和referent=ThreadLocal。ThreadLocal是被弱引用关联。
//ThreadLocal由强引用变成了弱引用,因为Entry继承了WeakReference,在Entry的构造方法中,调用了super(k)方法就会将threadLocal实例包装成一个WeakReferenece。
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;//value是真正需要存储的Object。
//ThreadLocalMap的Entry本身就是虚引用,
//WeakReference<B> weakReference = new WeakReference<B>(b1, rq); b1=null之后,weakReference就回去排队。
//Entry继承WeakReference,ThreadLocal=k释放了整个Entry就会去排队。
Entry(ThreadLocal<?> k, Object v) {//ThreadLocal放在WeakReference里面。
super(k);//ThreadLocal=null被gc后该 Entry 就会进入到 ReferenceQueue 中
value = v;
}
}
private static final int INITIAL_CAPACITY = 16;
private Entry[] table;
private int size = 0;//实际个数
private int threshold;
private void setThreshold(int len) {//加载因子为2/3,所以哈希表可用大小为:16*2/3=10,即哈希表可用容量为10。
threshold = len * 2 / 3;//threshold是总共容量的2/3。
}
private static int nextIndex(int i, int len) {//i+1对len取余
return ((i + 1 < len) ? i + 1 : 0);
}
private static int prevIndex(int i, int len) {
return ((i - 1 >= 0) ? i - 1 : len - 1);//循环
}
//ThreadLocalMap是线程的属性,key是一个个的ThreadLocal,value是值。
ThreadLocalMap1(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);//threshold是总共容量的2/3。
} private ThreadLocalMap1(ThreadLocalMap1 parentMap) {
Entry[] parentTable = parentMap.table;//获取table
int len = parentTable.length;//设置容量大小
setThreshold(len);//设置threshold
table = new Entry[len]; for (int j = 0; j < len; j++) {
Entry e = parentTable[j];//获取每一个Entry
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();//获取key是ThreadLocal
if (key != null) {
Object value = key.childValue(e.value);//e.value获取的是value
Entry c = new Entry(key, value);//构造新的Entry
int h = key.threadLocalHashCode & (len - 1);//计算table的索引
while (table[h] != null)
h = nextIndex(h, len);//索引加一重新计算索引
table[h] = c;//放入table
size++;
}
}
}
} 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// hash冲突
return getEntryAfterMiss(key, i, e);
} //ThreadLocalMap 中采用开放定址法,所以当前 key 的散列值和元素在数组中的索引并不一定完全对应。
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {//e是i位置的值,为null直接退出 ,null后面有key相等也不管了null为边界。所以get(61)就get不到了
ThreadLocal<?> k = e.get();
if (k == key)
return e;
if (k == null)//get时候遇到脏数据擦除 。因为强引用的ThreadLocal在外部置位了空。擦除这个位置时候,还会向右以null为边,擦除其他脏数据。
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
} //set也会擦除脏数据。设置时候不一定是新增,有可能是之前已经有了去替换
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
//哈希表大小总是为2的幂次方,所以相与等同于一个取模的过程,
int i = key.threadLocalHashCode & (len-1); //如果不考虑remove,所有跟i相关的都在i一起,并且是以null为边界的。考虑remove,所有跟i一样的是在一起并且null为边界,但是中间有可能有null。 //e=null退出说明没有冲突,不为null说明冲突了。 线性探测。 向右移动,null为边界
for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {//环形查找,不会死循环,因为会扩容。死循环是正常数据占据了全部位置。
ThreadLocal<?> k = e.get();
if (k == key) {//相等就覆盖
e.value = value;
return;
}
//放到脏数据位置,脏数据后面有key相等的也不管了。放脏数据位置时候会向右一直找到null看有没有key相等的null后面有key相等的也不管了。最后放到脏数据位置。
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
//i个位置e=null,直接放
tab[i] = new Entry(key, value);
int sz = ++size;
//从i位置开始清除,清除掉了就不用hash,没有清除掉就hash。
if (!cleanSomeSlots(i, sz) && sz >= threshold)//size > len * 2 / 3
rehash();
} private void remove(ThreadLocal<?> key) {
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)]) {//null后面有key相等的也不清除了。
if (e.get() == key) {//是根据ThreadLocal=key来判断是否同一个。
e.clear();//置WeakReference里面强引用referent=ThreadLocal=null,WeakReference里面清除引用关联只能clear()。因为是私有的。
expungeStaleEntry(i);//清除i位置,并向右以null为界清除其他脏数据。
return;
}
}
} //替换到脏数据staleSlot位置:也不是放到脏数据位置,而是从staleSlot开始向右一直到null先找一遍,有key相等就交换 并放到staleSlot并清除这个位置,否则放到脏数据staleSlot位置。
//replaceStaleEntry并不仅仅局限于处理当前已知的脏entry,它认为在出现脏entry的相邻位置也有很大概率出现脏entry,所以为了一次处理到位,就需要向前环形搜索,找到前面的脏entry。
private void replaceStaleEntry(ThreadLocal<?> key, Object value, int staleSlot) {
Entry[] tab = table;
int len = tab.length;//总长度,不是实际长度。
Entry e; int slotToExpunge = staleSlot; //slotToExpunge是准备要清理的位置。
//slotToExpunge=null为边界左边最远脏数据位置。e都不为null就是死循环。所以肯定有null的。null在最后几个。remove也会有null。
//循环都是以null作为边界的。null后面有key相等也不要了
for (int i = prevIndex(staleSlot, len);/*往前移动一个位置,会又走回来 */ (e = tab[i]) != null; i = prevIndex(i, len))
if (e.get() == null)
slotToExpunge = i; // 清除数据也是以null作为边界的。 // 向右找,null就退出,中间有key相等的就停止。都不为null也没有相等的就死循环。
for (int i = nextIndex(staleSlot, len);/*往右移动一个位置,会又走回来 */ (e = tab[i]) != null; i = nextIndex(i, len)) {
ThreadLocal<?> k = e.get(); if (k == key) {//向右移动,找到key相等的就去覆盖。 key跟i位置相等
e.value = value; tab[i] = tab[staleSlot];//staleSlot是脏数据位置。i位置是key一样的位置。
tab[staleSlot] = e;//e还是放在了staleSlot位置。i这个key相等的位置直接被脏数据覆盖了。 //slotToExpunge == staleSlot不可能向左找又回来了, 找回来就是全部不是null,就是死循环。
if (slotToExpunge == staleSlot)//向左没有找到脏数据。第一个for循环没有改变slotToExpunge的值。
slotToExpunge = i;//清除的位置为i,否则清除位置是左边的脏数据。肯定要以左边的清除位置为准,因为可以清除的更多
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);//expungeStaleEntry(slotToExpunge)清除slotToExpunge位置,
//slotToExpunge到返回值位置都已经没有脏数据,cleanSomeSlots(expungeStaleEntry(slotToExpunge), len)是从返回值位置开始清除脏数据
return;
} if (k == null && slotToExpunge == staleSlot)//slotToExpunge == staleSlot说明向左找又回来了, 说明e都不为null,会是死循环。
slotToExpunge = i;//左边没有脏数据右边i是脏数据。第一个for循环没有改变slotToExpunge的值。 清除的位置变为i。否则清除位置是左边的脏数据。肯定要以左边的清除位置为准,因为可以清除的更多。
}
//像右走,一直到e=null位置,都没有找到key相等的位置。就放到staleSlot脏数据位置。
tab[staleSlot].value = null;
tab[staleSlot] = new Entry(key, value); // staleSlot是放数据的位置,不能清除。
if (slotToExpunge != staleSlot)
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
} //清除i位置,并且i向右一直到null清除或者重新找位置
private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;//局部变量是为了增强引用,不回收。
int len = tab.length; // staleSlot位置 置位null。
tab[staleSlot].value = null;//Entry里面的value解除关系,Entry里面的referent已经解除关系了。
tab[staleSlot] = null;//Entry断掉引用关系,
size--; // staleSlot位置移除可,往后一直在null,Rehash
Entry e;
int i;
//上面把staleSlot位置置为null了。从staleSlot向右一个个看,key为null就清除,不为null就重新算位置。e=null退出循环。
for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) {
ThreadLocal<?> k = e.get();//get()方法返回的是referent=ThreadLocal。
if (k == null) {//e不为null key是null就移除,
e.value = null;
tab[i] = null;//value移除,entyr解除关系,ThreadLocal已经解除关系为null了。
size--;
} else {
int h = k.threadLocalHashCode & (len - 1);
if (h != i) {//重新找位置h
tab[i] = null; while (tab[h] != null)
h = nextIndex(h, len);//h有元素,重新找h
tab[h] = e;//放到h的位置上去
}
}
}
return i;//旧i和新i之间全部没有脏数据了新i是null的位置。
} //本来只准备i开始移动log2(n)次,移动过程中发现有脏数据,就改变移动次数再次移动log2(len)次。
private boolean cleanSomeSlots(int i, int n) {//新插入的位置i和实际大小n
boolean removed = false;//并没有真正的清除,只是找到了要清除的位置,而真正的清除在 expungeStaleEntry(int staleSlot) 里面
Entry[] tab = table;
int len = tab.length;
do {
i = nextIndex(i, len);//从i开始一个个向后看。循环log2(n)趟i一直加到i+log2(n),可以循环回来。
Entry e = tab[i];
if (e != null && e.get() == null) {
n = len;//如果在扫描过程中遇到脏entry的话就会令n为当前hash表的长度(n=len),再扫描log2(n)趟,
//注意此时n增加无非就是多增加了循环次数,增加循环次数就是i向右增加到更大的位置而已。
removed = true;
i = expungeStaleEntry(i);//清除i位置,并且i向右一直到null清除或者重新找位置。旧i到新i之间全部没有脏数据了下次从新i开始就可以了
}
//执行 对数次数 数量的扫描,是一种 基于不扫描(快速但保留垃圾)和 所有元素扫描之间的平衡。
} while ( (n >>>= 1) != 0);//n除以2,n用来控制扫描趟数(循环次数),在扫描过程中,如果没有遇到脏entry就整个扫描过程持续log2(n)次,log2(n)的得来是因为n >>>= 1,每次n右移一位相当于n除以2。
//
return removed;
} private void rehash() {//首先会清理陈旧的 Entry,如果清理完之后元素数量仍然大于 threshold 的 3/4,则进行扩容操作(数组大小变为原来的 2倍)
expungeStaleEntries(); if (size >= threshold - threshold / 4) //size >= 3/4*threshold, size >= 1/2*len
resize();//扩容
} private void resize() {
Entry[] oldTab = table;
int oldLen = oldTab.length;
int newLen = oldLen * 2;
Entry[] newTab = new Entry[newLen];
int count = 0; for (int j = 0; j < oldLen; ++j) {
Entry e = oldTab[j];
if (e != null) {
ThreadLocal<?> k = e.get();
if (k == null) {//遍历过程中如果遇到脏entry的话直接另value为null,有助于value能够被回收
e.value = null; // 帮助GC
} else {//重新hash
int h = k.threadLocalHashCode & (newLen - 1);
while (newTab[h] != null)
h = nextIndex(h, newLen);
newTab[h] = e;
count++;
}
}
} setThreshold(newLen);
size = count;
table = newTab;
} /**
*/
private void expungeStaleEntries() {
Entry[] tab = table;
int len = tab.length;
for (int j = 0; j < len; j++) {//遍历所有的元素
Entry e = tab[j];
if (e != null && e.get() == null)//key为null的有问题元素。
expungeStaleEntry(j);
}
}
}
}

ThreadLocal源代码3的更多相关文章

  1. Java ThreadLocal 源代码分析

    Java ThreadLocal 之前在写SSM项目的时候使用过一个叫PageHelper的插件 可以自动完成分页而不用手动写SQL limit 用起来大概是这样的 最开始的时候觉得很困惑,因为直接使 ...

  2. 多线程之美2一ThreadLocal源代码分析

    目录结构 1.应用场景及作用 2.结构关系 2.1.三者关系类图 2.2.ThreadLocalMap结构图 2.3. 内存引用关系 2.4.存在内存泄漏原因 3.源码分析 3.1.重要代码片段 3. ...

  3. ThreadLocal源代码2

    private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); } private stat ...

  4. ThreadLocal源代码1

    public class ThreadLocalTrxt { static ThreadLocal<Object> x1 = new ThreadLocal<Object>() ...

  5. 另一鲜为人知的单例写法-ThreadLocal

    另一鲜为人知的单例写法-ThreadLocal 源代码范例 当我阅读FocusFinder和Choreographer的时候,我发现这两类的单例实现和我们寻经常使用双重检查锁非常不一样.而是用来一个T ...

  6. java ThreadLocal(应用场景及使用方式及原理)

    尽管ThreadLocal与并发问题相关,可是很多程序猿只将它作为一种用于"方便传參"的工具,胖哥觉得这或许并非ThreadLocal设计的目的,它本身是为线程安全和某些特定场景的 ...

  7. ThreadLocal分析

    我们再介绍一个在多线程环境中经常使用的类ThreadLocal,它是java为解决多线程程序的并发问题提供了一种新的方向,使用这个ThreadLocal类可以帮助开发者很简单地编写出简洁的程序,并且是 ...

  8. ThreadLocal深入理解与内存泄露分析

    ThreadLocal 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本.所以每个线程都能够独立地改变自己的副本.而不会影响其他线程所相应的副本. ...

  9. 深入理解线程本地变量ThreadLocal

    ThreadLocal理解: 假设在多线程并发环境中.一个可变对象涉及到共享与竞争,那么该可变对象就一定会涉及到线程间同步操作,这是多线程并发问题. 否则该可变对象将作为线程私有对象,可通过Threa ...

随机推荐

  1. java 泛型 类型作为参量 Class<T> transform

    Class<T> transform T:作为类型,用于定义变量: transform:作为具体类的类:用于创建实例. 类型信息是脱敏的具体类: 可以使用class的具体功能: 不能使用具 ...

  2. Spring的三大核心思想:IOC(控制反转),DI(依赖注入),AOP(面向切面编程)

    Spring核心思想,IoC与DI详解(如果还不明白,放弃java吧) 1.IoC是什么?    IoC(Inversion of Control)控制反转,IoC是一种新的Java编程模式,目前很多 ...

  3. 一种动态的样式语言--Less 之 命名空间

    LESS 命名空间 如果想更好的组织CSS或者单纯是为了更好的封闭,将一些变量或者混合模块打包起来,你像下面这样在#bundle中定义一些属性集之后可以重复使用: #bundle{ .button() ...

  4. ES6学习笔记--default,rest

    default 意思是默认值.大家可以看下面的例子,调用animal()方法时忘记了传参数,传统的做法就是加上这一句type= type || 'cat' 来指定默认值. function anima ...

  5. Find the Weak Connected Component in the Directed Graph

    Description Find the number Weak Connected Component in the directed graph. Each node in the graph c ...

  6. PinPoint使用教程

    选择该应用的展示边界 InBound:3 OutBound:3 基本概念 APM (Application Performance Management/应用性能管理)工具 为大规模分布式系统. 开发 ...

  7. linux学习14 Linux运维高级系统应用-glob通配及IO重定向

    一.回顾 1.bash基础特性:命令补全,路径补全,命令引用 2.文件或目录的复制,移动及删除操作 3.变量:变量类型 存储格式,数据表示范围,参与运算 二.bash的基础特性 1.globbing: ...

  8. itertools 高效的循环

    在打印内容字节数较小时,全部载入内存后,再打印,没有问题.可是,如果现在有成千上百万条车辆行驶轨迹,叫你分析出其中每个客户的出行规律,堵车情况等,假如是在单机上处理这件事. 你可能首先要面临,也可能被 ...

  9. Spring中静态方法中使用@Resource注解的变量

    开发中,有些时候可能会工具类的静态方法,而这个静态方法中又使用到了@Resource注解后的变量.如果要直接使用 Utils.staticMethod(),项目会报异常:如果不直接使用,还要先 new ...

  10. C复习---动态内存分配

    原型extern void *malloc(unsigned int num_bytes);头文件#include <stdlib.h>#include <malloc.h>函 ...