@@ -5331,7 +5331,7 @@ transient int size;
53315331
53325332 * `(n - 1) & hash`:计算下标位置
53335333
5334- 
5334+ <img src=" https://gitee.com/seazean/images/raw/master/Java/HashMap-putVal哈希运算.png" style="zoom:80%;" />
53355335
53365336 总结: hashcode 转化为32位二进制,高16 bit和低16 bit做了一个异或
53375337
@@ -5442,17 +5442,37 @@ transient int size;
54425442
544354434. resize
54445444
5445- 当HashMap中的元素个数超过数组大小(数组长度)*loadFactor(负载因子)时,就会进行数组扩容。扩容会伴随着一次重新hash分配,并且会遍历hash表中所有的元素,非常耗时,所以要尽量避免resize
5446-
5447- 扩容时split方法会将树**拆成高位和低位两个链表**,判断长度是否小于等于6,小于则变成非树节点
5445+ 当HashMap中的元素个数超过(数组长度)*loadFactor(负载因子)`时,就会进行数组扩容。扩容会伴随着一次重新hash分配,并且会遍历hash表中所有的元素,非常耗时,所以要尽量避免resize
54485446
54495447 HashMap在进行扩容时,使用的rehash方式非常巧妙,因为每次扩容都是翻倍,与原来计算的 (n-1)&hash的结果相比,只是多了一个bit位,节点**要么就在原来的位置,要么就被分配到"原位置+旧容量"的位置**
5450-
5451- 判断:当前数组长度n为1的位为 x,如果key的哈希值 x 位也为1,则扩容后的索引为 now + n
5452-
5448+
5449+ 判断:e.hash与oldCap对应的有效高位上的值是1,即当前数组长度n为1的位为 x,如果key的哈希值 x 位也为1,则扩容后的索引为 now + n
5450+
54535451 注意:这里也要求**数组长度2的幂**
54545452
5455- 
5453+ 
5454+
5455+ 红黑树节点:扩容时split方法会将树**拆成高位和低位两个链表**,判断长度是否小于等于6
5456+
5457+ ```java
5458+ //如果低位链表首节点不为null,说明有这个链表存在
5459+ if (loHead != null) {
5460+ //如果链表下的元素小于等于6
5461+ if (lc <= UNTREEIFY_THRESHOLD)
5462+ //那就从红黑树转链表了,低位链表,迁移到新数组中下标不变,还是等于原数组到下标
5463+ tab[index] = loHead.untreeify(map);
5464+ else {
5465+ //低位链表,迁移到新数组中下标不变,把低位链表整个赋值到这个下标下
5466+ tab[index] = loHead;
5467+ //如果高位首节点不为空,说明原来的红黑树已经被拆分成两个链表了
5468+ if (hiHead != null)
5469+ //需要构建新的红黑树了
5470+ loHead.treeify(tab);
5471+ }
5472+ }
5473+ ```
5474+
5475+
54565476
545754774. remove
54585478 删除是首先先找到元素的位置,如果是链表就遍历链表找到元素之后删除。如果是用红黑树就遍历树然后找到之后做删除,树小于6的时候要转链表
@@ -14872,7 +14892,7 @@ public class Test14 {
1487214892`public static boolean interrupted()`:判断当前线程是否被打断,清除打断标记
1487314893`public boolean isInterrupted()`:判断当前线程是否被打断,不清除打断标记
1487414894
14875- sleep,wait,join方法都会让线程进入阻塞 (Waiting) 状态 ,打断进程会**清空打断状态** (false)
14895+ sleep,wait,join方法都会让线程进入阻塞状态 ,打断进程会**清空打断状态** (false)
1487614896
1487714897```java
1487814898public static void main(String[] args) throws InterruptedException {
@@ -15780,6 +15800,8 @@ class BigRoom {
1578015800
1578115801##### 死锁
1578215802
15803+ ###### 形成
15804+
1578315805死锁:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放,由于线程被无限期地阻塞,因此程序不可能正常终止
1578415806
1578515807java 死锁产生的四个必要条件:
@@ -15791,7 +15813,7 @@ java 死锁产生的四个必要条件:
1579115813四个条件都成立的时候,便形成死锁。死锁情况下打破上述任何一个条件,便可让死锁消失。
1579215814
1579315815```java
15794- public class ThreadDead {
15816+ public class Dead {
1579515817 public static Object resources1 = new Object();
1579615818 public static Object resources2 = new Object();
1579715819 public static void main(String[] args) {
@@ -15858,7 +15880,9 @@ class HoldLockThread implements Runnable {
1585815880
1585915881
1586015882
15861- **定位死锁**:
15883+ ###### 定位
15884+
15885+ 定位死锁的方法:
1586215886
1586315887* 检测死锁可以使用 jconsole工具,或者使用 jps 定位进程 id,再用 `jstack id`定位死锁
1586415888
@@ -15898,7 +15922,7 @@ class HoldLockThread implements Runnable {
1589815922
1589915923* linux 下可以通过 top 先定位到CPU 占用高的 Java 进程,再利用 `top -Hp 进程id` 来定位是哪个线程,最后再用 jstack 排查
1590015924
15901- 避免死锁:避免死锁要注意加锁顺序
15925+ * 避免死锁:避免死锁要注意加锁顺序
1590215926
1590315927
1590415928
@@ -16948,23 +16972,50 @@ final class Message {
1694816972
1694916973### JMM
1695016974
16975+ #### 模型
16976+
1695116977Java 内存模型是 Java MemoryModel(JMM),本身是一种**抽象的概念**,实际上并不存在,描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式
1695216978
1695316979JMM主要是为了规定了线程和内存之间的一些关系,根据JMM的设计,系统存在一个主内存(Main Memory),Java中所有变量都存储在主存中,对于所有线程都是共享的;每条线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些**变量的拷贝**,线程对所有变量的操作都是先对变量进行拷贝,然后在工作内存中进行,不能直接操作主内存中的变量;线程之间无法相互直接访问,线程间的通信(传递)必须通过主内存来完成
1695416980
1695516981
1695616982
16983+ 主内存和工作内存:
16984+
16985+ * 主内存:计算机的内存,也就是经常提到的8G内存,16G内存
16986+ * 工作内存:多个线程同时访问变量时,每个线程都会拷贝一份到各自的工作内存
16987+
1695716988**jvm和jmm之间的关系**:
1695816989
1695916990* jmm中的主内存、工作内存与jvm中的Java堆、栈、方法区等并不是同一个层次的内存划分
1696016991* 这两者基本上是没有关系的,如果两者一定要勉强对应起来,那从变量、主内存、工作内存的定义来看:
1696116992 * 主内存主要对应于Java堆中的对象实例数据部分,而工作内存则对应于虚拟机栈中的部分区域
1696216993 * 从更低层次上说,主内存就直接对应于物理硬件的内存,而为了获取更好的运行速度,虚拟机(甚至是硬件系统本身的优化措施)可能会让工作内存优先存储于寄存器和高速缓存中,因为程序运行时主要访问读写的是工作内存
1696316994
16964- 主内存和工作内存:
1696516995
16966- * 主内存:计算机的内存,也就是经常提到的8G内存,16G内存
16967- * 工作内存:多个线程同时访问变量时,每个线程都会拷贝一份到各自的工作内存
16996+
16997+
16998+
16999+ #### 交互
17000+
17001+ Java 内存模型定义了 8 个操作来完成主内存和工作内存的交互操作:
17002+
17003+ <img src="https://gitee.com/seazean/images/raw/master/Java/JMM内存交互.png" style="zoom: 67%;" />
17004+
17005+ * read:把一个变量的值从主内存传输到工作内存中
17006+ * load:在 read 之后执行,把 read 得到的值放入工作内存的变量副本中
17007+ * use:把工作内存中一个变量的值传递给执行引擎
17008+ * assign:把一个从执行引擎接收到的值赋给工作内存的变量
17009+ * store:把工作内存的一个变量的值传送到主内存中
17010+ * write:在 store 之后执行,把 store 得到的值放入主内存的变量中
17011+ * lock:作用于主内存的变量
17012+ * unlock
17013+
17014+
17015+
17016+
17017+
17018+ #### 特性
1696817019
1696917020**JMM特性**:
1697017021
0 commit comments