diff --git a/MD/ConcurrentHashMap.md b/MD/ConcurrentHashMap.md index 1e3220a4..0a9d7546 100644 --- a/MD/ConcurrentHashMap.md +++ b/MD/ConcurrentHashMap.md @@ -9,7 +9,7 @@ ## JDK1.7 实现 ### 数据结构 -![](https://ws2.sinaimg.cn/large/006tNc79ly1fn2f5pgxinj30dw0730t7.jpg) +![](https://i.loli.net/2019/05/08/5cd1d2c5ce95c.jpg) 如图所示,是由 `Segment` 数组、`HashEntry` 数组组成,和 `HashMap` 一样,仍然是数组加链表组成。 @@ -58,13 +58,14 @@ ## JDK1.8 实现 -![](https://ws3.sinaimg.cn/large/006tNc79gy1fthpv4odbsj30lp0drmxr.jpg) +![](https://i.loli.net/2019/05/08/5cd1d2ce33795.jpg) 1.8 中的 ConcurrentHashMap 数据结构和实现与 1.7 还是有着明显的差异。 其中抛弃了原有的 Segment 分段锁,而采用了 `CAS + synchronized` 来保证并发安全性。 -![](https://ws3.sinaimg.cn/large/006tNc79gy1fthq78e5gqj30nr09mmz9.jpg) +![](https://s2.loli.net/2024/05/21/MVr92SEeJI34fas.png) + 也将 1.7 中存放数据的 HashEntry 改为 Node,但作用都是相同的。 @@ -74,7 +75,8 @@ 重点来看看 put 函数: -![](https://ws3.sinaimg.cn/large/006tNc79gy1fthrz8jlo8j30oc0rbte3.jpg) +![](https://s2.loli.net/2024/05/21/EpBRMOQnD8bx2wH.png) + - 根据 key 计算出 hashcode 。 - 判断是否需要进行初始化。 @@ -85,7 +87,8 @@ ### get 方法 -![](https://ws1.sinaimg.cn/large/006tNc79gy1fthsnp2f35j30o409hwg7.jpg) +![](https://s2.loli.net/2024/05/21/CFvAuGp8BMUko6I.png) + - 根据计算出来的 hashcode 寻址,如果就在桶上那么直接返回值。 - 如果是红黑树那就按照树的方式获取值。 @@ -93,4 +96,4 @@ ## 总结 -1.8 在 1.7 的数据结构上做了大的改动,采用红黑树之后可以保证查询效率(`O(logn)`),甚至取消了 ReentrantLock 改为了 synchronized,这样可以看出在新版的 JDK 中对 synchronized 优化是很到位的。 +1.8 在 1.7 的数据结构上做了大的改动,采用红黑树之后可以保证查询效率(`O(logn)`),甚至取消了 ReentrantLock 改为了 synchronized,这样可以看出在新版的 JDK 中对 synchronized 优化是很到位的。 \ No newline at end of file diff --git a/MD/HashMap.md b/MD/HashMap.md index bcebd2d5..d975c728 100644 --- a/MD/HashMap.md +++ b/MD/HashMap.md @@ -4,7 +4,7 @@ > 以下基于 JDK1.7 分析。 -![](https://ws2.sinaimg.cn/large/006tNc79gy1fn84b0ftj4j30eb0560sv.jpg) +![](https://i.loli.net/2019/05/08/5cd1d2be77958.jpg) 如图所示,HashMap 底层是基于数组和链表实现的。其中有两个重要的参数: @@ -61,7 +61,7 @@ map.forEach((key,value)->{ 并发场景发生扩容,调用 `resize()` 方法里的 `rehash()` 时,容易出现环形链表。这样当获取一个不存在的 `key` 时,计算出的 `index` 正好是环形链表的下标时就会出现死循环。 -![](https://ws2.sinaimg.cn/large/006tNc79gy1fn85u0a0d9j30n20ii0tp.jpg) +![](https://i.loli.net/2019/05/08/5cd1d2c4ede54.jpg) > 所以 HashMap 只能在单线程中使用,并且尽量的预设容量,尽可能的减少扩容。 diff --git a/MD/LinkedList.md b/MD/LinkedList.md index e4d25b3b..3002d8a3 100644 --- a/MD/LinkedList.md +++ b/MD/LinkedList.md @@ -1,6 +1,6 @@ # LinkedList 底层分析 -![](https://ws4.sinaimg.cn/large/006tKfTcly1fqzb66c00gj30p7056q38.jpg) +![](https://i.loli.net/2019/07/04/5d1cdc7b0c7d526575.jpg) 如图所示 `LinkedList` 底层是基于双向链表实现的,也是实现了 `List` 接口,所以也拥有 List 的一些特点(JDK1.7/8 之后取消了循环,修改为双向链表)。 diff --git a/MD/ThreadPoolExecutor.md b/MD/ThreadPoolExecutor.md index 616601b1..5e912626 100644 --- a/MD/ThreadPoolExecutor.md +++ b/MD/ThreadPoolExecutor.md @@ -1,10 +1,10 @@ -![](https://ws1.sinaimg.cn/large/006tKfTcgy1ftpwh3a2szj31kw11xh84.jpg) + ## 前言 平时接触过多线程开发的童鞋应该都或多或少了解过线程池,之前发布的《阿里巴巴 Java 手册》里也有一条: -![](https://ws2.sinaimg.cn/large/006tKfTcgy1ftpxf3x1epj30la03s0tl.jpg) +![](https://s2.loli.net/2024/05/21/H7oVe3Xqz8c2pWJ.png) 可见线程池的重要性。 @@ -67,7 +67,8 @@ threadPool.execute(new Job()); 在具体分析之前先了解下线程池中所定义的状态,这些状态都和线程的执行密切相关: -![](https://ws3.sinaimg.cn/large/006tKfTcgy1ftq1ks5qywj30jn03i3za.jpg) +![](https://s2.loli.net/2024/05/21/Kf7kDlFUQy816eV.png) + - `RUNNING` 自然是运行状态,指可以接受任务执行队列里的任务 - `SHUTDOWN` 指调用了 `shutdown()` 方法,不再接受新任务了,但是队列里的任务得执行完毕。 @@ -77,11 +78,13 @@ threadPool.execute(new Job()); 用图表示为: -![](https://ws4.sinaimg.cn/large/006tKfTcgy1ftq2nxlwe5j30sp0ba0ts.jpg) +![](https://s2.loli.net/2024/05/21/U2tQ3RWN5CnaquJ.png) + 然后看看 `execute()` 方法是如何处理的: -![](https://ws1.sinaimg.cn/large/006tKfTcgy1ftq283zi91j30ky08mwgb.jpg) +![](https://s2.loli.net/2024/05/21/Fa6ogDun8wkbAes.png) + 1. 获取当前线程池的状态。 2. 当前线程数量小于 coreSize 时创建一个新的线程运行。 @@ -92,7 +95,8 @@ threadPool.execute(new Job()); 这里借助《聊聊并发》的一张图来描述这个流程: -![](https://ws4.sinaimg.cn/large/006tKfTcgy1ftq2vzuv5rj30dw085q3i.jpg) +![](https://s2.loli.net/2024/05/21/hNXE42uOroLlDRY.png) + ### 如何配置线程 @@ -103,7 +107,7 @@ threadPool.execute(new Job()); 通常我们是需要根据这批任务执行的性质来确定的。 -- IO 密集型任务:由于线程并不是一直在运行,所以可以尽可能的多配置线程,比如 CPU 个数 * 2 +- IO 密集型任务:由于线程并不是一直在运行,所以可以尽可能的多配置线程,比如 CPU 个数 * 2 - CPU 密集型任务(大量复杂的运算)应当分配较少的线程,比如 CPU 个数相当的大小。 @@ -206,16 +210,18 @@ public class TreadPoolConfig { 其实 ThreadPool 本身已经提供了不少 api 可以获取线程状态: -![](https://ws1.sinaimg.cn/large/006tKfTcgy1ftq3xsrbs6j30bg0bpgnb.jpg) +![](https://s2.loli.net/2024/05/21/8YJ9ULEWFfBqR2k.png) + 很多方法看名字就知道其含义,只需要将这些信息暴露到 SpringBoot 的监控端点中,我们就可以在可视化页面查看当前的线程池状态了。 甚至我们可以继承线程池扩展其中的几个函数来自定义监控逻辑: -![](https://ws2.sinaimg.cn/large/006tKfTcgy1ftq40lkw9jj30mq07rmyt.jpg) +![](https://s2.loli.net/2024/05/21/l1YjPUmvFqeHW3n.png) + +![](https://s2.loli.net/2024/05/21/jKGwm679LinTW3y.png) -![](https://ws4.sinaimg.cn/large/006tKfTcgy1ftq41asf8rj30kq07cabd.jpg) 看这些名称和定义都知道,这是让子类来实现的。 @@ -383,7 +389,8 @@ public class CommandUser extends HystrixCommand { 运行结果: -![](https://ws2.sinaimg.cn/large/006tKfTcgy1ftq4e0ukubj30ps04gtak.jpg) +![](https://s2.loli.net/2024/05/21/kJL2ZYFv4o6nP7y.png) + 可以看到两个任务分成了两个线程池运行,他们之间互不干扰。 @@ -397,7 +404,8 @@ public class CommandUser extends HystrixCommand { 通过刚才的构造函数也能证明: -![](https://ws2.sinaimg.cn/large/006tKfTcgy1ftq4i6xy2qj30uo09adhp.jpg) +![](https://s2.loli.net/2024/05/21/uW1eDmV3CGipI2F.png) + 还要注意的一点是: @@ -409,4 +417,4 @@ public class CommandUser extends HystrixCommand { 文末的 hystrix 源码: -[https://github.com/crossoverJie/Java-Interview/tree/master/src/main/java/com/crossoverjie/hystrix](https://github.com/crossoverJie/Java-Interview/tree/master/src/main/java/com/crossoverjie/hystrix) +[https://github.com/crossoverJie/Java-Interview/tree/master/src/main/java/com/crossoverjie/hystrix](https://github.com/crossoverJie/Java-Interview/tree/master/src/main/java/com/crossoverjie/hystrix) \ No newline at end of file diff --git a/MD/Threadcore.md b/MD/Threadcore.md index 65c394d2..1a4f8d56 100644 --- a/MD/Threadcore.md +++ b/MD/Threadcore.md @@ -48,7 +48,7 @@ public final boolean compareAndSet(long expect, long update) { 现代计算机中,由于 `CPU` 直接从主内存中读取数据的效率不高,所以都会对应的 `CPU` 高速缓存,先将主内存中的数据读取到缓存中,线程修改数据之后首先更新到缓存,之后才会更新到主内存。如果此时还没有将数据更新到主内存其他的线程此时来读取就是修改之前的数据。 -![](https://ws2.sinaimg.cn/large/006tKfTcly1fmouu3fpokj31ae0osjt1.jpg) +![](https://i.loli.net/2019/05/08/5cd1c6a9c6546.jpg) 如上图所示。 diff --git a/MD/collection/LinkedHashMap.md b/MD/collection/LinkedHashMap.md index fa55eb5b..7ccde30e 100644 --- a/MD/collection/LinkedHashMap.md +++ b/MD/collection/LinkedHashMap.md @@ -31,7 +31,7 @@ 调试可以看到 `map` 的组成: -![](https://ws2.sinaimg.cn/large/006tKfTcly1fo6l9xp91lj319m0s4tgi.jpg) +![](https://i.loli.net/2019/05/08/5cd1ba2adf7c0.jpg) 打开源码可以看到: @@ -66,7 +66,7 @@ 上边的 demo 总结成一张图如下: -![](https://ws1.sinaimg.cn/large/006tKfTcgy1fodggwc523j30za0n4wgj.jpg) +![](https://i.loli.net/2019/05/08/5cd1ba2d418b6.jpg) 第一个类似于 `HashMap` 的结构,利用 `Entry` 中的 `next` 指针进行关联。 diff --git a/README.md b/README.md index 4462fc0d..9e8df602 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@
- +
[![Build Status](https://travis-ci.org/crossoverJie/JCSprout.svg?branch=master)](https://travis-ci.org/crossoverJie/JCSprout) @@ -19,6 +19,15 @@
+
+ +202405171520366.png +
+ +最近开通了知识星球,感谢大家对 `JCSprout` 的支持,为大家提供 100 份 10 元优惠券,也就是 69-10=59 元,具体福利大家可以扫码参考再决定是否加入。 + +> PS: 后续会继续维护该项目,同时加入现在热门的 Golang/kubernetes/OpenTelemetry 等知识点,感兴趣的可以加入星球当面催更(当然内容也会更新到这个项目里)。 + | 📊 |⚔️ | 🖥 | 🚏 | 🏖 | 🌁| 📮 | 🔍 | 🚀 | 🌈 |💡 | :--------: | :---------: | :---------: | :---------: | :---------: | :---------:| :---------: | :-------: | :-------:| :------:|:------:| diff --git a/docs/README.md b/docs/README.md index ff56f8e0..6cf49e92 100644 --- a/docs/README.md +++ b/docs/README.md @@ -27,6 +27,15 @@
+
+ +202405171520366.png +
+ +最近开通了知识星球,感谢大家对 `JCSprout` 的支持,为大家提供 100 份 10 元优惠券,也就是 69-10=59 元,具体福利大家可以扫码参考再决定是否加入。 + +> PS: 后续会继续维护该项目,同时加入现在热门的 Golang/kubernetes/OpenTelemetry 等知识点,感兴趣的可以加入星球当面催更(当然内容也会更新到这个项目里)。 + [个人博客](https://crossoverjie.top) [Twitter](https://twitter.com/crossoverJie) diff --git a/docs/thread/ConcurrentHashMap.md b/docs/thread/ConcurrentHashMap.md index adc905ad..0a9d7546 100644 --- a/docs/thread/ConcurrentHashMap.md +++ b/docs/thread/ConcurrentHashMap.md @@ -9,7 +9,7 @@ ## JDK1.7 实现 ### 数据结构 -![](https://i.loli.net/2019/07/19/5d313f7215c4240040.jpg) +![](https://i.loli.net/2019/05/08/5cd1d2c5ce95c.jpg) 如图所示,是由 `Segment` 数组、`HashEntry` 数组组成,和 `HashMap` 一样,仍然是数组加链表组成。 @@ -58,13 +58,14 @@ ## JDK1.8 实现 -![](https://i.loli.net/2019/07/19/5d313f751f85f13539.jpg) +![](https://i.loli.net/2019/05/08/5cd1d2ce33795.jpg) 1.8 中的 ConcurrentHashMap 数据结构和实现与 1.7 还是有着明显的差异。 其中抛弃了原有的 Segment 分段锁,而采用了 `CAS + synchronized` 来保证并发安全性。 -![](https://i.loli.net/2019/07/19/5d313f76c30a232619.jpg) +![](https://s2.loli.net/2024/05/21/MVr92SEeJI34fas.png) + 也将 1.7 中存放数据的 HashEntry 改为 Node,但作用都是相同的。 @@ -74,7 +75,8 @@ 重点来看看 put 函数: -![](https://i.loli.net/2019/07/19/5d313f78c31dc41505.jpg) +![](https://s2.loli.net/2024/05/21/EpBRMOQnD8bx2wH.png) + - 根据 key 计算出 hashcode 。 - 判断是否需要进行初始化。 @@ -85,7 +87,8 @@ ### get 方法 -![](https://i.loli.net/2019/07/19/5d313f7aab95b41715.jpg) +![](https://s2.loli.net/2024/05/21/CFvAuGp8BMUko6I.png) + - 根据计算出来的 hashcode 寻址,如果就在桶上那么直接返回值。 - 如果是红黑树那就按照树的方式获取值。 @@ -93,4 +96,4 @@ ## 总结 -1.8 在 1.7 的数据结构上做了大的改动,采用红黑树之后可以保证查询效率(`O(logn)`),甚至取消了 ReentrantLock 改为了 synchronized,这样可以看出在新版的 JDK 中对 synchronized 优化是很到位的。 +1.8 在 1.7 的数据结构上做了大的改动,采用红黑树之后可以保证查询效率(`O(logn)`),甚至取消了 ReentrantLock 改为了 synchronized,这样可以看出在新版的 JDK 中对 synchronized 优化是很到位的。 \ No newline at end of file diff --git a/docs/thread/ThreadPoolExecutor.md b/docs/thread/ThreadPoolExecutor.md index 54fe3a30..5e912626 100644 --- a/docs/thread/ThreadPoolExecutor.md +++ b/docs/thread/ThreadPoolExecutor.md @@ -1,10 +1,10 @@ -![](https://i.loli.net/2019/05/08/5cd1d2a867bea.jpg) + ## 前言 平时接触过多线程开发的童鞋应该都或多或少了解过线程池,之前发布的《阿里巴巴 Java 手册》里也有一条: -![](https://i.loli.net/2019/05/08/5cd1d2a8dad64.jpg) +![](https://s2.loli.net/2024/05/21/H7oVe3Xqz8c2pWJ.png) 可见线程池的重要性。 @@ -26,7 +26,6 @@ - `Executors.newFixedThreadPool(nThreads)`:创建固定大小的线程池。 - `Executors.newSingleThreadExecutor()`:创建单个线程的线程池。 - 其实看这三种方式创建的源码就会发现: @@ -68,7 +67,8 @@ threadPool.execute(new Job()); 在具体分析之前先了解下线程池中所定义的状态,这些状态都和线程的执行密切相关: -![](https://i.loli.net/2019/05/08/5cd1d2a9bc566.jpg) +![](https://s2.loli.net/2024/05/21/Kf7kDlFUQy816eV.png) + - `RUNNING` 自然是运行状态,指可以接受任务执行队列里的任务 - `SHUTDOWN` 指调用了 `shutdown()` 方法,不再接受新任务了,但是队列里的任务得执行完毕。 @@ -78,22 +78,25 @@ threadPool.execute(new Job()); 用图表示为: -![](https://i.loli.net/2019/05/08/5cd1d2aa81655.jpg) +![](https://s2.loli.net/2024/05/21/U2tQ3RWN5CnaquJ.png) + 然后看看 `execute()` 方法是如何处理的: -![](https://i.loli.net/2019/05/08/5cd1d2ab921db.jpg) +![](https://s2.loli.net/2024/05/21/Fa6ogDun8wkbAes.png) + 1. 获取当前线程池的状态。 2. 当前线程数量小于 coreSize 时创建一个新的线程运行。 3. 如果当前线程处于运行状态,并且写入阻塞队列成功。 -4. 双重检查,再次获取线程状态;如果线程状态变了(非运行状态)就需要从阻塞队列移除任务,并尝试判断线程是否全部执行完毕。同时执行拒绝策略。 +4. 双重检查,再次获取线程池状态;如果线程池状态变了(非运行状态)就需要从阻塞队列移除任务,并尝试判断线程是否全部执行完毕。同时执行拒绝策略。 5. 如果当前线程池为空就新创建一个线程并执行。 6. 如果在第三步的判断为非运行状态,尝试新建线程,如果失败则执行拒绝策略。 这里借助《聊聊并发》的一张图来描述这个流程: -![](https://i.loli.net/2019/05/08/5cd1d2ac0936c.jpg) +![](https://s2.loli.net/2024/05/21/hNXE42uOroLlDRY.png) + ### 如何配置线程 @@ -104,7 +107,7 @@ threadPool.execute(new Job()); 通常我们是需要根据这批任务执行的性质来确定的。 -- IO 密集型任务:由于线程并不是一直在运行,所以可以尽可能的多配置线程,比如 CPU 个数 * 2 +- IO 密集型任务:由于线程并不是一直在运行,所以可以尽可能的多配置线程,比如 CPU 个数 * 2 - CPU 密集型任务(大量复杂的运算)应当分配较少的线程,比如 CPU 个数相当的大小。 @@ -207,16 +210,18 @@ public class TreadPoolConfig { 其实 ThreadPool 本身已经提供了不少 api 可以获取线程状态: -![](https://i.loli.net/2019/05/08/5cd1d2accbbcf.jpg) +![](https://s2.loli.net/2024/05/21/8YJ9ULEWFfBqR2k.png) + 很多方法看名字就知道其含义,只需要将这些信息暴露到 SpringBoot 的监控端点中,我们就可以在可视化页面查看当前的线程池状态了。 甚至我们可以继承线程池扩展其中的几个函数来自定义监控逻辑: -![](https://i.loli.net/2019/05/08/5cd1d2add4d31.jpg) +![](https://s2.loli.net/2024/05/21/l1YjPUmvFqeHW3n.png) + +![](https://s2.loli.net/2024/05/21/jKGwm679LinTW3y.png) -![](https://i.loli.net/2019/05/08/5cd1d2aeea439.jpg) 看这些名称和定义都知道,这是让子类来实现的。 @@ -384,7 +389,8 @@ public class CommandUser extends HystrixCommand { 运行结果: -![](https://i.loli.net/2019/05/08/5cd1d2b06ef2d.jpg) +![](https://s2.loli.net/2024/05/21/kJL2ZYFv4o6nP7y.png) + 可以看到两个任务分成了两个线程池运行,他们之间互不干扰。 @@ -398,7 +404,8 @@ public class CommandUser extends HystrixCommand { 通过刚才的构造函数也能证明: -![](https://i.loli.net/2019/05/08/5cd1d2b69cd32.jpg) +![](https://s2.loli.net/2024/05/21/uW1eDmV3CGipI2F.png) + 还要注意的一点是: diff --git a/pom.xml b/pom.xml index 97db5bdd..7a2e2885 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.crossoverjie.interview - interview + JCSprout 1.0.0-SNAPSHOT jar