[]( )2\. nextHashCode方法
------------------------------------------------------------------------------------
用于产生ThreadLocal的哈希值。
**ThreadLocal.java中的相关代码:**
private static int nextHashCode() {
// 将nextHashCode的值加上HASH_INCREMENT的值,并返回
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
[]( )3.构造方法
------------------------------------------------------------------------
**ThreadLocal.java中的相关代码:**
public ThreadLocal() {
}
[]( )4\. initialValue方法
------------------------------------------------------------------------------------
用于为ThreadLocal提供当前线程的初始化值。
默认为空,如果有需要,可以重写该方法。
**ThreadLocal.java中的相关代码:**
protected T initialValue() {
return null;
}
[]( )5\. getMap方法
------------------------------------------------------------------------------
获取指定线程的ThreadLocalMap。
**ThreadLocal.java中的相关代码:**
ThreadLocalMap getMap(Thread t) {
// 返回线程的全局变量
return t.threadLocals;
}
[]( )6\. createMap方法
---------------------------------------------------------------------------------
为指定线程创建ThreadLocalMap用于保存线程间共享的对象,并存储firstValue。
**ThreadLocal.java中的相关代码:**
void createMap(Thread t, T firstValue) {
// 创建ThreadLocalMap对象
// 保存到线程的全局变量threadLocals中
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
[]( )7\. createInheritedMap方法
------------------------------------------------------------------------------------------
该方法用于根据父线程的ThreadLocalMap对象创建子线程继承的ThreadLocalMap对象。
**ThreadLocal.java中的相关代码:**
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
// 调用构造方法,创建新对象
return new ThreadLocalMap(parentMap);
}
[]( )8\. childValue方法
----------------------------------------------------------------------------------
该方法在InheritableThreadLocal类中被重写,用于根据父值而给出子值。
该方法为了在必要时继承的子类可以不用重写ThreadLocalMap类,而通过此方法实现原本的功能。
**ThreadLocal.java中的相关代码:**
T childValue(T parentValue) {
throw new UnsupportedOperationException();
}
[]( )9\. remove方法
------------------------------------------------------------------------------
删除当前线程在ThreadLocal中存储的值。
**ThreadLocal.java中的相关代码:**
public void remove() {
// 获取当前线程的ThreadLocalMap对象
ThreadLocalMap m = getMap(Thread.currentThread());
// 若ThreadLocalMap不为空
if (m != null)
m.remove(this); // 移除当前线程存储的值
}
[]( )10\. set方法
----------------------------------------------------------------------------
保存值到当前线程的ThreadLocalMap中。
**ThreadLocal.java中的相关代码:**
public void set(T value) {
// 获取当前线程
Thread t = Thread.currentThread();
// 获取当前线程的ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
// 若ThreadLocalMap对象存在
if (map != null)
// 直接保存value
map.set(this, value);
else // 若ThreadLocalMap对象不存在
// 则创建ThreadLocalMap对象,并保存value
createMap(t, value);
}
[]( )11\. setInitialValue方法
----------------------------------------------------------------------------------------
用于将当前线程的ThreadLocalMap存储的值设置为初始值。
**ThreadLocal.java中的相关代码:**
private T setInitialValue() {
// 获取初始值
T value = initialValue();
// 获取当前线程
Thread t = Thread.currentThread();
// 获取当前线程的ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
// 若ThreadLocalMap对象存在
if (map != null)
map.set(this, value); // 则保存值value
else // 若ThreadLocalMap对象不存在
createMap(t, value); // 则创建ThreadLocalMap对象,再保存值value
// 返回初始值
return value;
}
[]( )12\. get方法
----------------------------------------------------------------------------
获取当前线程的ThreadLocalMap中对应的值。
**ThreadLocal.java中的相关代码:**
public T get() {
// 获取当前线程
Thread t = Thread.currentThread();
// 获取当前线程的ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
// 若ThreadLocalMap对象存在
if (map != null) {
// 尝试获取当前ThreadLocal对应的键值对
ThreadLocalMap.Entry e = map.getEntry(this);
// 若键值对存在,说明之前存储过
if (e != null) {
@SuppressWarnings("unchecked")
// 获取键值对的值
T result = (T)e.value;
// 返回
return result;
}
}
// 若ThreadLocalMap对象不存在,
// 则将ThreadLocal对应的值设置为初始值,并返回
return setInitialValue();
}
[]( )13.ThreadLocal中的静态内部类ThreadLocalMap
-----------------------------------------------------------------------------------------------------
ThreadLocalMap是ThreadLocal用于存储键值对的内部类,ThreadLocal中对数据操作都是通过该内部类提供的方法实现的。
**ThreadLocal.java中的相关代码:**
static class ThreadLocalMap {
…
}
### []( )1)全局变量
**ThreadLocal.java中的相关代码:**
// 初始化容量,表示能够存储键值对的数量,必须是2的次方形式
private static final int INITIAL_CAPACITY = 16;
// 用于存储键值对的数组
private Entry[] table;
// 用于记录已经存储的键值对的数量
private int size = 0;
// 扩容的阈值,存储的键值对数量超过该值将会触发扩容,默认为0
private int threshold;
### []( )2)ThreadLocalMap中的静态内部类Entry
该类用于存储键值对,其中键的类型为ThreadLocal,值的类型为Object。
同时,该类继承了弱引用类WeakReference,当获取键为空时,说明该键的引用被回收。
**ThreadLocal.java中的相关代码:**
static class Entry extends WeakReference<ThreadLocal<?>> {
// 线程中存储的值
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
### []( )3)构造方法
#### []( )i)参数为键值对
该方法会创建ThreadLocalMap,并将参数中的键值对进行保存。
**ThreadLocal.java中的相关代码:**
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
// 根据初始化容量,创建键值对数组
table = new Entry[INITIAL_CAPACITY];
// 获取ThreadLocal的哈希值,和INITIAL_CAPACITY – 1进行与运算
// 计算键值对在数组中的位置
// INITIAL_CAPACITY – 1作为掩码
// 如16-1=15,1111,最后进行与运算得到的数值一定在0到15之间
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
// 创建新的键值对,保存到数组对应的位置中
table[i] = new Entry(firstKey, firstValue);
// 记录当前键值对数量1
size = 1;
// 设置扩容阈值
setThreshold(INITIAL_CAPACITY);
}
#### []( )ii)参数为ThreadLocalMap
该方法用来根据继承的ThreadLocalMap的内容创建新的ThreadLocalMap。
该方法是私有的,仅供createInheritedMap方法调用。
**ThreadLocal.java中的相关代码:**
private ThreadLocalMap(ThreadLocalMap parentMap) {
// 获取继承的ThreadLocalMap中的键值对数组
Entry[] parentTable = parentMap.table;
// 获取数组长度
int len = parentTable.length;
// 根据长度设置扩容阈值
setThreshold(len);
// 根据长度创建新数组
table = new Entry[len];
// 循环,将键值对转移到新数组中
for (int j = 0; j < len; j++) {
// 获取键值对
Entry e = parentTable[j];
// 若j位置键值对存在
if (e != null) {
@SuppressWarnings("unchecked")
// 获取键
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
// 若键存在
if (key != null) {
// 根据键,获取值
Object value = key.childValue(e.value);
// 根据键和值,创建新的键值对
Entry c = new Entry(key, value);
// 根据键ThreadLocal的哈希值
// 计算出键值对在数组中的位置h
// len - 1作为掩码
// 如16-1=15,1111,
// 最后进行与运算得到的数值一定在0到15之间
int h = key.threadLocalHashCode & (len - 1);
// 循环,若数组的h位置有键值对
// 则根据当前位置h和数组长度,获取下一个位置
// 直到找到一个没有键值对的位置
while (table[h] != null)
h = nextIndex(h, len);
// 将键值对存在对应位置
table[h] = c;
// 当前存储的键值对数量加1
size++;
}
}
}
}
### []( )4)setThreshold方法
用来设置扩容阈值。
**ThreadLocal.java中的相关代码:**
private void setThreshold(int len) {
// 阈值为数组长度的三分之二
threshold = len * 2 / 3;
}
### []( )5)nextIndex方法
获取位置i的下一个位置。
**ThreadLocal.java中的相关代码:**
private static int nextIndex(int i, int len) {
// 若i的下一个位置i+1小于数组长度len
// 则返回i+1,否则返回0
// 该操作相当于i对len取模
return ((i + 1 < len) ? i + 1 : 0);
}
### []( )6)prevIndex方法
获取位置i的上一个位置。
**ThreadLocal.java中的相关代码:**
private static int prevIndex(int i, int len) {
// 若i的上一个位置i-1大于0
// 则返回i-1,否则返回len-1
return ((i - 1 >= 0) ? i - 1 : len - 1);
}
### []( )7)expungeStaleEntry方法
删除指定位置的键值对,并对其它位置的键值对重新散列整理。
**ThreadLocal.java中的相关代码:**
private int expungeStaleEntry(int staleSlot) {
// 获取数组
Entry[] tab = table;
// 获取数组的长度
int len = tab.length;
// 删除指定位置的键值对
tab[staleSlot].value = null;
tab[staleSlot] = null;
// 存储的键值对数量减1
size--;
// 对数组循环整理,重新散列,
// 直到遇到为空的位置,停止循环
Entry e;
int i;
// 从staleSlot下一个位置i开始
// 若i位置的键值对e为空,则跳出循环
// 每次循环i向后移动一个位置,若移动到最后一个位置,则回到0开始
for (i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
// 从i位置的键值对e中获取键k
ThreadLocal<?> k = e.get();
// 若键为空
if (k == null) {
// 清除当前位置的键值对
e.value = null;
tab[i] = null;
// 存储的键值对数量减1
size--;
} else { // 若键不为空
// 重新计算键值对应该存放的位置h
int h = k.threadLocalHashCode & (len - 1);
// 若键值对应该存储的位置和当前存放的位置不一致
if (h != i) {
// 对当前的位置进行清空
tab[i] = null;
// 循环,从键值对应该存放的位置h开始找
// 找到一个空位置,存放键值对
while (tab[h] != null)
h = nextIndex(h, len);
tab[h] = e;
}
}
}
// 返回跳出循环时键值对为空的位置
return i;
}
### []( )8)cleanSomeSlots方法
从位置i的下一位开始,向下查看log2(n)个位置,如果发现位置不对的键值对,则对其进行清理。
**ThreadLocal.java中的相关代码:**
private boolean cleanSomeSlots(int i, int n) {
// 标志位,如果进行过清理,则为true
boolean removed = false;
// 获取键值对数组
Entry[] tab = table;
// 获取数组长度
int len = tab.length;
// 循环
do {
// 获取位置i的下一个位置,结果再赋给i
i = nextIndex(i, len);
// 获取该位置的键值对
Entry e = tab[i];
// 若该位置的键值对存在,并且键值对的键不存在
if (e != null && e.get() == null) {
// 重新设置循环次数
n = len;
// 设置标志位
removed = true;
// 删除指定位置的键值对,并进行整理
i = expungeStaleEntry(i);
}
} while ( (n >>>= 1) != 0); // 循环log2(n)次
// 返回结果
return removed;
}
### []( )9)expungeStaleEntries方法
删除数组中键不存在的键值对,对位置错误的键值对进行调整。
**ThreadLocal.java中的相关代码:**
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)
// 删除该位置的键值对,并整理。
expungeStaleEntry(j);
}
}
### []( )10)resize方法
用于对键值对数组扩容,扩大之前容量的2倍。
**ThreadLocal.java中的相关代码:**
private void resize() {
// 获取旧的键值对数组
Entry[] oldTab = table;
// 获取旧的键值对数组的长度
int oldLen = oldTab.length;
// 根据旧长度计算新长度,新长度为旧长度的2倍
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) {
// 则将其值删除,即释放引用
e.value = null; // 可以帮助垃圾回收器快点回收
} else { // 若键存在
// 重新计算其在新数组中的位置
int h = k.threadLocalHashCode & (newLen - 1);
// 若该位置已经被占了,则循环查找下一个可以存放的位置
while (newTab[h] != null)
h = nextIndex(h, newLen);
// 存放键值对到新数组中
newTab[h] = e;
// 新数组已经使用的空间数量加1
count++;
}
}
}
// 设置新的扩容阈值
setThreshold(newLen);
// 将局部变量保存到全局变量
// 将已经使用的空间数量保存到全局变量
size = count;
// 将新数组保存到全局变量
table = newTab;
}
### []( )11)rehash方法
对所有的键值对进行整理,删除键不存在的键值对。
如果整理后键值对的数量还是过高,则进行扩容。
**ThreadLocal.java中的相关代码:**
private void rehash() {
// 整理所有键值对。
expungeStaleEntries();
// 当数组已经使用的空间数量超过阈值的四分之三
// 即总空间的六分之一,则进行扩容
// 使用更低的阈值避免滞后现象
if (size >= threshold - threshold / 4)
resize();
}
### []( )12)replaceStaleEntry方法
**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**
**深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**
**因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
![img](https://img-blog.csdnimg.cn/img_convert/a23179f03ac36d5e58c45de69c429d46.jpeg)
![img](https://img-blog.csdnimg.cn/img_convert/346d2e3803a6eb518b25fbcdc501720a.png)
![img](https://img-blog.csdnimg.cn/img_convert/b2c18a031e1c3a76226913dcd2c575f2.png)
![img](https://img-blog.csdnimg.cn/img_convert/f735648dd938ff70b35b2c408a1e75e7.png)
![img](https://img-blog.csdnimg.cn/img_convert/d36ef5d30aa85edf6e85275857b5034e.png)
![img](https://img-blog.csdnimg.cn/img_convert/1e29fa2955475ce7de527fcf0fa772df.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!**
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**
**如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)**
![img](https://img-blog.csdnimg.cn/img_convert/1c050b3f69e9cc5b96583080119c9771.jpeg)
## 最后:学习总结——MyBtis知识脑图(纯手绘xmind文档)
学完之后,若是想验收效果如何,其实最好的方法就是可自己去总结一下。比如我就会在学习完一个东西之后自己去手绘一份xmind文件的知识梳理大纲脑图,这样也可方便后续的复习,且都是自己的理解,相信随便瞟几眼就能迅速过完整个知识,脑补回来。下方即为我手绘的MyBtis知识脑图,由于是xmind文件,不好上传,所以小编将其以图片形式导出来传在此处,细节方面不是特别清晰。但可给感兴趣的朋友提供完整的MyBtis知识脑图原件(包括上方的面试解析xmind文档)
![image](https://img-blog.csdnimg.cn/img_convert/5b235d307ba59bc1091bea0a8822a431.webp?x-oss-process=image/format,png)
除此之外,前文所提及的Alibaba珍藏版mybatis手写文档以及一本小小的MyBatis源码分析文档——《MyBatis源码分析》等等相关的学习笔记文档,也皆可分享给认可的朋友!
**一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
![img](https://img-blog.csdnimg.cn/img_convert/f88815777fcb1e4d550e8c562ffa8a0b.jpeg)
漫长,而且极易碰到天花板技术停滞不前!**
**因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
[外链图片转存中...(img-GTG4Z4jG-1712880230524)]
[外链图片转存中...(img-x51Gzty2-1712880230525)]
[外链图片转存中...(img-8HoemXw5-1712880230525)]
[外链图片转存中...(img-fmYx4UdZ-1712880230526)]
[外链图片转存中...(img-uUilFJHC-1712880230526)]
[外链图片转存中...(img-6esaUk3a-1712880230526)]
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!**
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**
**如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)**
[外链图片转存中...(img-SQdcb3EY-1712880230527)]
## 最后:学习总结——MyBtis知识脑图(纯手绘xmind文档)
学完之后,若是想验收效果如何,其实最好的方法就是可自己去总结一下。比如我就会在学习完一个东西之后自己去手绘一份xmind文件的知识梳理大纲脑图,这样也可方便后续的复习,且都是自己的理解,相信随便瞟几眼就能迅速过完整个知识,脑补回来。下方即为我手绘的MyBtis知识脑图,由于是xmind文件,不好上传,所以小编将其以图片形式导出来传在此处,细节方面不是特别清晰。但可给感兴趣的朋友提供完整的MyBtis知识脑图原件(包括上方的面试解析xmind文档)
[外链图片转存中...(img-69ftSlE5-1712880230527)]
除此之外,前文所提及的Alibaba珍藏版mybatis手写文档以及一本小小的MyBatis源码分析文档——《MyBatis源码分析》等等相关的学习笔记文档,也皆可分享给认可的朋友!
**一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
[外链图片转存中...(img-LaPMcgeO-1712880230527)]