1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-08-01 16:28:03 +08:00

Compare commits

...

7 Commits

Author SHA1 Message Date
Guide
c800fc05bf [docs update]完善java并发面试题&标注重要的问题 2024-09-25 11:37:00 +08:00
Guide
ad8458a23a
Merge pull request #2493 from qksuki/main
Update mysql-questions-01.md
2024-09-25 10:43:27 +08:00
Guide
e6b2c3c541
Update test.yml 2024-09-25 10:14:36 +08:00
Guide
461d487519 [docs update] redis 有序集合底层实现完善 2024-09-25 07:45:38 +08:00
qksuki
e84117b8f3
Update mysql-questions-01.md
修正描述问题
2024-09-24 23:00:34 +08:00
Guide
510a8239c4
Merge pull request #2491 from Mister-Hope/main
feat: bump deps
2024-09-24 10:22:33 +08:00
Mr.Hope
70819c92cb feat: bump deps 2024-09-24 00:09:56 +08:00
30 changed files with 2123 additions and 1231 deletions

View File

@ -13,14 +13,14 @@ jobs:
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v2
uses: pnpm/action-setup@v4
with:
run_install: true
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 20.17.0
cache: pnpm
- name: Build test

View File

@ -61,4 +61,5 @@ export default defineUserConfig({
pagePatterns: ["**/*.md", "!**/*.snippet.md", "!.vuepress", "!node_modules"],
shouldPrefetch: false,
shouldPreload: false,
});

View File

@ -0,0 +1 @@
$theme-color: #2980b9;

View File

@ -1,5 +1,4 @@
$theme-color: #2980b9;
$sidebar-width: 20rem;
$sidebar-mobile-width: 16rem;
$font-family: 'Georgia, -apple-system, "Nimbus Roman No9 L", "PingFang SC", "Hiragino Sans GB", "Noto Serif SC", "Microsoft Yahei", "WenQuanYi Micro Hei", sans-serif';
$font-family-heading: 'Georgia, -apple-system, "Nimbus Roman No9 L", "PingFang SC", "Hiragino Sans GB", "Noto Serif SC", "Microsoft Yahei", "WenQuanYi Micro Hei", sans-serif';
$vp-font: 'Georgia, -apple-system, "Nimbus Roman No9 L", "PingFang SC", "Hiragino Sans GB", "Noto Serif SC", "Microsoft Yahei", "WenQuanYi Micro Hei", sans-serif';
$vp-font-heading: 'Georgia, -apple-system, "Nimbus Roman No9 L", "PingFang SC", "Hiragino Sans GB", "Noto Serif SC", "Microsoft Yahei", "WenQuanYi Micro Hei", sans-serif';

View File

@ -42,28 +42,6 @@ export default hopeTheme({
},
plugins: {
components: {
rootComponents: {
// https://plugin-components.vuejs.press/zh/guide/utilities/notice.html#%E7%94%A8%E6%B3%95
// notice: [
// {
// path: "/",
// title: "PDF面试资料2024版",
// showOnce: true,
// content:
// "2024最新版原创PDF面试资料来啦涵盖 Java 核心、数据库、缓存、分布式、设计模式、智力题等内容,非常全面!",
// actions: [
// {
// text: "点击领取",
// link: "https://oss.javaguide.cn/backend-notekbook/official-account-traffic-backend-notebook-with-data-screenshot.png",
// type: "primary",
// },
// ],
// },
// ],
},
},
blog: true,
copyright: {
@ -81,12 +59,13 @@ export default hopeTheme({
rss: true,
},
markdownTab: {
codeTabs: true,
},
mdEnhance: {
align: true,
codetabs: true,
figure: true,
gfm: true,
hint: true,
include: {
resolvePath: (file, cwd) => {
if (file.startsWith("@"))

View File

@ -0,0 +1,70 @@
---
title: 个人介绍 Q&A
category: 走近作者
---
<!-- @include: @small-advertisement.snippet.md -->
这篇文章我会通过 Q&A 的形式简单介绍一下我自己。
## 我是什么时候毕业的?
很多老读者应该比较清楚,我是 19 年本科毕业的,刚毕业就去了某家外企“养老”。
我的学校背景是比较差的,高考失利,勉强过了一本线 20 来分,去了荆州的一所很普通的双非一本。不过,还好我没有因为学校而放弃自己,反倒是比身边的同学都要更努力,整个大学还算过的比较充实。
下面这张是当时拍的毕业照(后排最中间的就是我):
![](https://oss.javaguide.cn/javaguide/%E4%B8%AA%E4%BA%BA%E4%BB%8B%E7%BB%8D.png)
## 我坚持写了多久博客?
时间真快啊!我自己是从大二开始写博客的。那时候就是随意地在博客平台上发发自己的学习笔记和自己写的程序。就比如 [谢希仁老师的《计算机网络》内容总结](../cs-basics/network/computer-network-xiexiren-summary.md) 这篇文章就是我在大二学习计算机网络这门课的时候对照着教材总结的。
身边也有很多小伙伴经常问我:“我现在写博客还晚么?”
我觉得哈!如果你想做什么事情,尽量少问迟不迟,多问自己值不值得,只要你觉得有意义,就尽快开始做吧!人生很奇妙,我们每一步的重大决定,都会对自己未来的人生轨迹产生影响。是好还是坏,也只有我们自己知道了!
对我自己来说,坚持写博客这一项决定对我人生轨迹产生的影响是非常正面的!所以,我也推荐大家养成坚持写博客的习惯。
## 我在大学期间赚了多少钱?
在校期间,我还通过办培训班、接私活、技术培训、编程竞赛等方式变现 20w+,成功实现“经济独立”。我用自己赚的钱去了重庆、三亚、恩施、青岛等地旅游,还给家里补贴了很多,减轻了父母的负担。
下面这张是我大一下学期办补习班的时候拍的(离开前的最后一顿饭):
![补习班的最后一顿晚餐](https://oss.javaguide.cn/p3-juejin/f36bfd719b9b4463b2f1d3edc51faa97~tplv-k3u1fbpfcp-zoom-1.jpeg)
下面这张是我大三去三亚的时候拍的:
![](https://oss.javaguide.cn/javaguide/psc.jpeg)
其实,我在大学就这么努力地开始赚钱,也主要是因为家庭条件太一般,父母赚钱都太辛苦了!也正是因为我自己迫切地想要减轻父母的负担,所以才会去尝试这么多赚钱的方法。
我发现做咱们程序员这行的,很多人的家庭条件都挺一般的,选择这个行业的很大原因不是因为自己喜欢,而是为了多赚点钱。
如果你也想通过接私活变现的话,可以在我的公众号后台回复“**接私活**”来了解一些我的个人经验分享。
::: center
![](https://oss.javaguide.cn/github/javaguide/gongzhonghaoxuanchuan.png)
:::
## 为什么自称 Guide
可能是因为我的项目名字叫做 JavaGuide , 所以导致有很多人称呼我为 **Guide 哥**
后面,为了读者更方便称呼,我就将自己的笔名改成了 **Guide**
我早期写文章用的笔名是 SnailClimb 。很多人不知道这个名字是啥意思给大家拆解一下就清楚了。SnailClimb=Snail蜗牛+Climb(攀登)。我从小就非常喜欢听周杰伦的歌曲,特别是他的《蜗牛》🐌 这首歌曲,另外,当年我高考发挥的算是比较失常,上了大学之后还算是比较“奋青”,所以,我就给自己起的笔名叫做 SnailClimb ,寓意自己要不断向上攀登,嘿嘿 😁
![](https://oss.javaguide.cn/p3-juejin/37599546f3b34b92a32db579a225aa45~tplv-k3u1fbpfcp-watermark.png)
## 后记
凡心所向,素履所往,生如逆旅,一苇以航。
生活本就是有苦有甜。共勉!
![JavaGuide 官方公众号](https://oss.javaguide.cn/github/javaguide/gongzhonghaoxuanchuan.png)

View File

@ -45,9 +45,11 @@ category: 走近作者
如果你也想通过接私活变现的话,可以在我的公众号后台回复“**接私活**”来了解一些我的个人经验分享。
<div align="center">
<img src="https://oss.javaguide.cn/github/javaguide/gongzhonghaoxuanchuan.png" style="margin: 0 auto;" />
</div>
::: center
![](https://oss.javaguide.cn/github/javaguide/gongzhonghaoxuanchuan.png)
:::
## 为什么自称 Guide

View File

@ -7,6 +7,8 @@ tag:
> 本文转自:<http://www.guoyaohua.com/sorting.html>JavaGuide 对其做了补充完善。
<!-- markdownlint-disable MD024 -->
## 引言
所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。排序算法,就是如何使得记录按照要求排列的方法。排序算法在很多领域得到相当地重视,尤其是在大量数据的处理方面。一个优秀的算法可以节省大量的资源。在各个领域中考虑到数据的各种限制和规范,要得到一个符合实际的优秀算法,得经过大量的推理和分析。

View File

@ -5,6 +5,8 @@ tag:
- 算法
---
<!-- markdownlint-disable MD024 -->
## 1. 两数相加
### 题目描述

View File

@ -43,13 +43,13 @@ tag:
<p style="text-align:center;font-size:13px;color:gray">https://www.itrelease.com/2018/07/advantages-and-disadvantages-of-personal-area-network-pan/</p>
12. **分组packet **:因特网中传送的数据单元。由首部 header 和数据段组成。分组又称为包,首部可称为包头。
13. **存储转发store and forward **:路由器收到一个分组,先检查分组是否正确,并过滤掉冲突包错误。确定包正确后,取出目的地址,通过查找表找到想要发送的输出端口地址,然后将该包发送出去。
11. **分组packet **:因特网中传送的数据单元。由首部 header 和数据段组成。分组又称为包,首部可称为包头。
12. **存储转发store and forward **:路由器收到一个分组,先检查分组是否正确,并过滤掉冲突包错误。确定包正确后,取出目的地址,通过查找表找到想要发送的输出端口地址,然后将该包发送出去。
![](https://oss.javaguide.cn/p3-juejin/addb6b2211444a4da9e0ffc129dd444f~tplv-k3u1fbpfcp-zoom-1.gif)
14. **带宽bandwidth**:在计算机网络中,表示在单位时间内从网络中的某一点到另一点所能通过的“最高数据率”。常用来表示网络的通信线路所能传送数据的能力。单位是“比特每秒”,记为 b/s。
15. **吞吐量throughput **:表示在单位时间内通过某个网络(或信道、接口)的数据量。吞吐量更经常地用于对现实世界中的网络的一种测量,以便知道实际上到底有多少数据量能够通过网络。吞吐量受网络的带宽或网络的额定速率的限制。
13. **带宽bandwidth**:在计算机网络中,表示在单位时间内从网络中的某一点到另一点所能通过的“最高数据率”。常用来表示网络的通信线路所能传送数据的能力。单位是“比特每秒”,记为 b/s。
14. **吞吐量throughput **:表示在单位时间内通过某个网络(或信道、接口)的数据量。吞吐量更经常地用于对现实世界中的网络的一种测量,以便知道实际上到底有多少数据量能够通过网络。吞吐量受网络的带宽或网络的额定速率的限制。
### 1.2. 重要知识点总结
@ -289,8 +289,8 @@ HTTP 协议的本质就是一种浏览器与服务器之间约定好的通信格
![](https://oss.javaguide.cn/p3-juejin/8e3efca026654874bde8be88c96e1783~tplv-k3u1fbpfcp-zoom-1.jpeg)
10. **代理服务器Proxy Server**代理服务器Proxy Server是一种网络实体它又称为万维网高速缓存。 代理服务器把最近的一些请求和响应暂存在本地磁盘中。当新请求到达时,若代理服务器发现这个请求与暂时存放的的请求相同,就返回暂存的响应,而不需要按 URL 的地址再次去互联网访问该资源。代理服务器可在客户端或服务器工作,也可以在中间系统工作。
11. **简单邮件传输协议(SMTP)** : SMTPSimple Mail Transfer Protocol即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。 SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。 通过 SMTP 协议所指定的服务器,就可以把 E-mail 寄到收信人的服务器上了整个过程只要几分钟。SMTP 服务器则是遵循 SMTP 协议的发送邮件服务器,用来发送或中转发出的电子邮件。
9. **代理服务器Proxy Server**代理服务器Proxy Server是一种网络实体它又称为万维网高速缓存。 代理服务器把最近的一些请求和响应暂存在本地磁盘中。当新请求到达时,若代理服务器发现这个请求与暂时存放的的请求相同,就返回暂存的响应,而不需要按 URL 的地址再次去互联网访问该资源。代理服务器可在客户端或服务器工作,也可以在中间系统工作。
10. **简单邮件传输协议(SMTP)** : SMTPSimple Mail Transfer Protocol即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。 SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。 通过 SMTP 协议所指定的服务器,就可以把 E-mail 寄到收信人的服务器上了整个过程只要几分钟。SMTP 服务器则是遵循 SMTP 协议的发送邮件服务器,用来发送或中转发出的电子邮件。
![一个电子邮件被发送的过程](https://oss.javaguide.cn/p3-juejin/2bdccb760474435aae52559f2ef9652f~tplv-k3u1fbpfcp-zoom-1.png)

View File

@ -55,6 +55,6 @@ SOHO 子网的“代理人”,也就是和外界的窗口,通常由路由器
3. WAN 的 ISP 变更接口地址时,无需通告 LAN 内主机。
4. LAN 主机对 WAN 不可见,不可直接寻址,可以保证一定程度的安全性。
然而NAT 协议由于其独特性,存在着一些争议。比如,可能你已经注意到了,<b>NAT 协议在 LAN 以外,标识一个内部主机时,使用的是端口号,因为 IP 地址都是相同的。</b>这种将端口号作为主机寻址的行为,可能会引发一些误会。此外,路由器作为网络层的设备,修改了传输层的分组内容(修改了源 IP 地址和端口号同样是不规范的行为。但是尽管如此NAT 协议作为 IPv4 时代的产物,极大地方便了一些本来棘手的问题,一直被沿用至今。
然而NAT 协议由于其独特性,存在着一些争议。比如,可能你已经注意到了,**NAT 协议在 LAN 以外,标识一个内部主机时,使用的是端口号,因为 IP 地址都是相同的。**这种将端口号作为主机寻址的行为,可能会引发一些误会。此外,路由器作为网络层的设备,修改了传输层的分组内容(修改了源 IP 地址和端口号同样是不规范的行为。但是尽管如此NAT 协议作为 IPv4 时代的产物,极大地方便了一些本来棘手的问题,一直被沿用至今。
<!-- @include: @article-footer.snippet.md -->

View File

@ -133,7 +133,7 @@ HTTP 状态码用于描述 HTTP 请求的结果,比如 2xx 就代表请求被
### HTTP Header 中常见的字段有哪些?
| 请求头字段名 | 说明 | 示例 |
| :------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------- |
| :------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------- |
| Accept | 能够接受的回应内容类型Content-Types。 | Accept: text/plain |
| Accept-Charset | 能够接受的字符集 | Accept-Charset: utf-8 |
| Accept-Datetime | 能够接受的按照时间来表示的版本 | Accept-Datetime: Thu, 31 May 2007 20:35:00 GMT |
@ -148,7 +148,7 @@ HTTP 状态码用于描述 HTTP 请求的结果,比如 2xx 就代表请求被
| Cookie | 之前由服务器通过 Set-Cookie下文详述发送的一个超文本传输协议 Cookie | Cookie: $Version=1; Skin=new; |
| Date | 发送该消息的日期和时间(按照 RFC 7231 中定义的"超文本传输协议日期"格式来发送) | Date: Tue, 15 Nov 1994 08:12:31 GMT |
| Expect | 表明客户端要求服务器做出特定的行为 | Expect: 100-continue |
| From | 发起此请求的用户的邮件地址 | From: [user@example.com](mailto:user@example.com) |
| From | 发起此请求的用户的邮件地址 | From: `user@example.com` |
| Host | 服务器的域名(用于虚拟主机),以及服务器所监听的传输控制协议端口号。如果所请求的端口是对应的服务的标准端口,则端口号可被省略。 | Host: en.wikipedia.org |
| If-Match | 仅当客户端提供的实体与服务器上对应的实体相匹配时,才进行对应的操作。主要作用是用于像 PUT 这样的方法中,仅当从用户上次更新某个资源以来,该资源未被修改的情况下,才更新该资源。 | If-Match: "737060cd8c284d8af7ad3082f209582d" |
| If-Modified-Since | 允许服务器在请求的资源自指定的日期以来未被修改的情况下返回 `304 Not Modified` 状态码 | If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT |
@ -156,11 +156,11 @@ HTTP 状态码用于描述 HTTP 请求的结果,比如 2xx 就代表请求被
| If-Range | 如果该实体未被修改过,则向我发送我所缺少的那一个或多个部分;否则,发送整个新的实体 | If-Range: "737060cd8c284d8af7ad3082f209582d" |
| If-Unmodified-Since | 仅当该实体自某个特定时间以来未被修改的情况下,才发送回应。 | If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT |
| Max-Forwards | 限制该消息可被代理及网关转发的次数。 | Max-Forwards: 10 |
| Origin | 发起一个针对跨来源资源共享的请求。 | Origin: [http://www.example-social-network.com](http://www.example-social-network.com/) |
| Origin | 发起一个针对跨来源资源共享的请求。 | `Origin: http://www.example-social-network.com` |
| Pragma | 与具体的实现相关,这些字段可能在请求/回应链中的任何时候产生多种效果。 | Pragma: no-cache |
| Proxy-Authorization | 用来向代理进行认证的认证信息。 | Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== |
| Range | 仅请求某个实体的一部分。字节偏移以 0 开始。参见字节服务。 | Range: bytes=500-999 |
| Referer | 表示浏览器所访问的前一个页面,正是那个页面上的某个链接将浏览器带到了当前所请求的这个页面。 | Referer: http://en.wikipedia.org/wiki/Main_Page |
| Referer | 表示浏览器所访问的前一个页面,正是那个页面上的某个链接将浏览器带到了当前所请求的这个页面。 | `Referer: http://en.wikipedia.org/wiki/Main_Page` |
| TE | 浏览器预期接受的传输编码方式:可使用回应协议头 Transfer-Encoding 字段中的值; | TE: trailers, deflate |
| Upgrade | 要求服务器升级到另一个协议。 | Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11 |
| User-Agent | 浏览器的浏览器身份标识字符串 | User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:12.0) Gecko/20100101 Firefox/21.0 |

View File

@ -606,7 +606,7 @@ InnoDB 的行锁是针对索引字段加的锁,表级锁是针对非索引字
InnoDB 行锁是通过对索引数据页上的记录加锁实现的MySQL InnoDB 支持三种行锁定方式:
- **记录锁Record Lock**也被称为记录锁,属于单个行记录上的锁。
- **记录锁Record Lock**:属于单个行记录上的锁。
- **间隙锁Gap Lock**:锁定一个范围,不包括记录本身。
- **临键锁Next-Key Lock**Record Lock+Gap Lock锁定一个范围包含记录本身主要目的是为了解决幻读问题MySQL 事务部分提到过)。记录锁只能锁住已经存在的记录,为了避免插入新记录,需要依赖间隙锁。

View File

@ -40,14 +40,14 @@ tag:
6) "60"
```
此时我们通过 `object` 指令查看 zset 的数据结构,可以看到当前有序集合存储的还是**ziplist(压缩列表)**。
此时我们通过 `object` 指令查看 zset 的数据结构,可以看到当前有序集合存储的还是**ziplist(压缩列表)**。
```bash
127.0.0.1:6379> object encoding rankList
"ziplist"
```
因为设计者考虑到 Redis 数据存放于内存,为了节约宝贵的内存空间在有序集合元素小于 64 字节且个数小于 128 的时候,会使用 ziplist而这个阈值的默认值的设置就来自下面这两个配置项。
因为设计者考虑到 Redis 数据存放于内存,为了节约宝贵的内存空间在有序集合元素小于 64 字节且个数小于 128 的时候,会使用 ziplist而这个阈值的默认值的设置就来自下面这两个配置项。
```bash
zset-max-ziplist-value 64

View File

@ -10,6 +10,8 @@ tag:
> - [Java 魔法类Unsafe 应用解析 - 美团技术团队 -2019](https://tech.meituan.com/2019/02/14/talk-about-java-magic-class-unsafe.html)
> - [Java 双刃剑之 Unsafe 类详解 - 码农参上 - 2021](https://xie.infoq.cn/article/8b6ed4195e475bfb32dacc5cb)
<!-- markdownlint-disable MD024 -->
阅读过 JUC 源码的同学,一定会发现很多并发工具类都调用了一个叫做 `Unsafe` 的类。
那这个类主要是用来干什么的呢?有什么使用场景呢?这篇文章就带你搞清楚!

View File

@ -14,6 +14,8 @@ head:
<!-- @include: @small-advertisement.snippet.md -->
<!-- markdownlint-disable MD024 -->
## 集合概述
### Java 集合概览

View File

@ -5,6 +5,8 @@ tag:
- Java并发
---
<!-- markdownlint-disable MD024 -->
## AQS 介绍
AQS 的全称为 `AbstractQueuedSynchronizer` ,翻译过来的意思就是抽象队列同步器。这个类在 `java.util.concurrent.locks` 包下面。

View File

@ -16,7 +16,7 @@ head:
## 线程
### 什么是线程和进程?
### ⭐️什么是线程和进程?
#### 何为进程?
@ -84,7 +84,7 @@ JDK 1.2 之前Java 线程是基于绿色线程Green Threads实现的
在 Windows 和 Linux 等主流操作系统中Java 线程采用的是一对一的线程模型,也就是一个 Java 线程对应一个系统内核线程。Solaris 系统是一个特例Solaris 系统本身就支持多对多的线程模型HotSpot VM 在 Solaris 上支持多对多和一对一。具体可以参考 R 大的回答: [JVM 中的线程模型是用户级的么?](https://www.zhihu.com/question/23096638/answer/29617153)。
### 请简要描述线程与进程的关系,区别及优缺点?
### ⭐️请简要描述线程与进程的关系,区别及优缺点?
下图是 Java 内存区域,通过下图我们从 JVM 的角度来说一下线程和进程之间的关系。
@ -130,7 +130,7 @@ JDK 1.2 之前Java 线程是基于绿色线程Green Threads实现的
关于这个问题的详细分析可以查看这篇文章:[大家都说 Java 有三种创建线程的方式!并发编程中的惊天骗局!](https://mp.weixin.qq.com/s/NspUsyhEmKnJ-4OprRFp9g)。
### 说说线程的生命周期和状态?
### ⭐️说说线程的生命周期和状态?
Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种不同状态的其中一个状态:
@ -216,7 +216,7 @@ new 一个 `Thread`,线程进入了新建状态。调用 `start()`方法,会
- **同步**:发出一个调用之后,在没有得到结果之前, 该调用就不可以返回,一直等待。
- **异步**:调用在发出之后,不用等待返回结果,该调用直接返回。
### 为什么要使用多线程?
### ⭐️为什么要使用多线程?
先从总体上来说:
@ -228,7 +228,7 @@ new 一个 `Thread`,线程进入了新建状态。调用 `start()`方法,会
- **单核时代**:在单核时代多线程主要是为了提高单进程利用 CPU 和 IO 系统的效率。 假设只运行了一个 Java 进程的情况,当我们请求 IO 的时候,如果 Java 进程中只有一个线程,此线程被 IO 阻塞则整个进程被阻塞。CPU 和 IO 设备只有一个在运行,那么可以简单地说系统整体效率只有 50%。当使用多线程的时候,一个线程被 IO 阻塞,其他线程还可以继续使用 CPU。从而提高了 Java 进程利用系统资源的整体效率。
- **多核时代**: 多核时代多线程主要是为了提高进程利用多核 CPU 的能力。举个例子:假如我们要计算一个复杂的任务,我们只用一个线程的话,不论系统有几个 CPU 核心,都只会有一个 CPU 核心被利用到。而创建多个线程,这些线程可以被映射到底层多个 CPU 核心上执行,在任务中的多个线程没有资源竞争的情况下,任务执行的效率会有显著性的提高,约等于(单核时执行时间/CPU 核心数)。
### 单核 CPU 支持 Java 多线程吗?
### ⭐️单核 CPU 支持 Java 多线程吗?
单核 CPU 是支持 Java 多线程的。操作系统通过时间片轮转的方式,将 CPU 的时间分配给不同的线程。尽管单核 CPU 一次只能执行一个任务,但通过快速在多个线程之间切换,可以让用户感觉多个任务是同时进行的。
@ -241,7 +241,7 @@ new 一个 `Thread`,线程进入了新建状态。调用 `start()`方法,会
Java 使用的线程调度是抢占式的。也就是说JVM 本身不负责线程的调度,而是将线程的调度委托给操作系统。操作系统通常会基于线程优先级和时间片来调度线程的执行,高优先级的线程通常获得 CPU 时间片的机会更多。
### 单核 CPU 上运行多个线程效率一定会高吗?
### ⭐️单核 CPU 上运行多个线程效率一定会高吗?
单核 CPU 同时运行多个线程的效率是否会高,取决于线程的类型和任务的性质。一般来说,有两种类型的线程:
@ -263,7 +263,7 @@ Java 使用的线程调度是抢占式的。也就是说JVM 本身不负责
- 线程安全指的是在多线程环境下,对于同一份数据,不管有多少个线程同时访问,都能保证这份数据的正确性和一致性。
- 线程不安全则表示在多线程环境下,对于同一份数据,多个线程同时访问时可能会导致数据混乱、错误或者丢失。
## 死锁
## ⭐️死锁
### 什么是线程死锁?

View File

@ -14,11 +14,11 @@ head:
<!-- @include: @article-header.snippet.md -->
## JMM(Java 内存模型)
## ⭐️JMM(Java 内存模型)
JMMJava 内存模型)相关的问题比较多,也比较重要,于是我单独抽了一篇文章来总结 JMM 相关的知识点和问题:[JMMJava 内存模型)详解](./jmm.md) 。
## volatile 关键字
## ⭐️volatile 关键字
### 如何保证变量的可见性?
@ -174,7 +174,7 @@ public void increase() {
}
```
## 乐观锁和悲观锁
## ⭐️乐观锁和悲观锁
### 什么是悲观锁?
@ -496,7 +496,7 @@ synchronized(this) {
另外,构造方法本身是线程安全的,但如果在构造方法中涉及到共享资源的操作,就需要采取适当的同步措施来保证整个构造过程的线程安全。
### synchronized 底层原理了解吗?
### ⭐️synchronized 底层原理了解吗?
synchronized 关键字底层原理属于 JVM 层面的东西。
@ -573,7 +573,7 @@ public class SynchronizedDemo2 {
`synchronized` 锁升级是一个比较复杂的过程,面试也很少问到,如果你想要详细了解的话,可以看看这篇文章:[浅析 synchronized 锁升级的原理与实现](https://www.cnblogs.com/star95/p/17542850.html)。
### synchronized 和 volatile 有什么区别?
### ⭐️synchronized 和 volatile 有什么区别?
`synchronized` 关键字和 `volatile` 关键字是两个互补的存在,而不是对立的存在!
@ -611,7 +611,7 @@ public ReentrantLock(boolean fair) {
- **公平锁** : 锁被释放之后,先申请的线程先得到锁。性能较差一些,因为公平锁为了保证时间上的绝对顺序,上下文切换更频繁。
- **非公平锁**:锁被释放之后,后申请的线程可能会先获取到锁,是随机或者按照其他优先级排序的。性能更好,但可能会导致某些线程永远无法获取到锁。
### synchronized 和 ReentrantLock 有什么区别?
### ⭐️synchronized 和 ReentrantLock 有什么区别?
#### 两者都是可重入锁

View File

@ -104,7 +104,7 @@ private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<S
};
```
### ThreadLocal 原理了解吗?
### ⭐️ThreadLocal 原理了解吗?
`Thread`类源代码入手。
@ -161,7 +161,7 @@ ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
![ThreadLocal内部类](https://oss.javaguide.cn/github/javaguide/java/concurrent/thread-local-inner-class.png)
### ThreadLocal 内存泄露问题是怎么导致的?
### ⭐️ThreadLocal 内存泄露问题是怎么导致的?
`ThreadLocalMap` 中使用的 key 为 `ThreadLocal` 的弱引用,而 value 是强引用。所以,如果 `ThreadLocal` 没有被外部强引用的情况下在垃圾回收的时候key 会被清理掉,而 value 不会被清理掉。
@ -191,7 +191,7 @@ static class Entry extends WeakReference<ThreadLocal<?>> {
顾名思义,线程池就是管理一系列线程的资源池。当有任务要处理时,直接从线程池中获取线程来处理,处理完之后线程并不会立即被销毁,而是等待下一个任务。
### 为什么要用线程池?
### ⭐️为什么要用线程池?
池化技术想必大家已经屡见不鲜了线程池、数据库连接池、HTTP 连接池等等都是对这个思想的应用。池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。
@ -222,7 +222,7 @@ static class Entry extends WeakReference<ThreadLocal<?>> {
- `CachedThreadPool` 可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。
- `ScheduledThreadPool`:给定的延迟后运行任务或者定期执行任务的线程池。
### 为什么不推荐使用内置线程池?
### ⭐️为什么不推荐使用内置线程池?
在《阿里巴巴 Java 开发手册》“并发处理”这一章节,明确指出线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
@ -270,7 +270,7 @@ public ScheduledThreadPoolExecutor(int corePoolSize) {
}
```
### 线程池常见参数有哪些?如何解释?
### ⭐️线程池常见参数有哪些?如何解释?
```java
/**
@ -322,11 +322,23 @@ public ScheduledThreadPoolExecutor(int corePoolSize) {
`ThreadPoolExecutor` 默认不会回收核心线程,即使它们已经空闲了。这是为了减少创建线程的开销,因为核心线程通常是要长期保持活跃的。但是,如果线程池是被用于周期性使用的场景,且频率不高(周期之间有明显的空闲时间),可以考虑将 `allowCoreThreadTimeOut(boolean value)` 方法的参数设置为 `true`,这样就会回收空闲(时间间隔由 `keepAliveTime` 指定)的核心线程了。
```java
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 6, 6, TimeUnit.SECONDS, new SynchronousQueue<>());
threadPoolExecutor.allowCoreThreadTimeOut(true);
public void allowCoreThreadTimeOut(boolean value) {
// 核心线程的 keepAliveTime 必须大于 0 才能启用超时机制
if (value && keepAliveTime <= 0) {
throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
}
// 设置 allowCoreThreadTimeOut 的值
if (value != allowCoreThreadTimeOut) {
allowCoreThreadTimeOut = value;
// 如果启用了超时机制,清理所有空闲的线程,包括核心线程
if (value) {
interruptIdleWorkers();
}
}
}
```
### 线程池的拒绝策略有哪些?
### ⭐️线程池的拒绝策略有哪些?
如果当前同时运行的线程数量达到最大线程数量并且队列也已经被放满了任务时,`ThreadPoolExecutor` 定义一些策略:
@ -518,7 +530,7 @@ new RejectedExecutionHandler() {
- `DelayedWorkQueue`(延迟队列):`ScheduledThreadPool``SingleThreadScheduledExecutor``DelayedWorkQueue` 的内部元素并不是按照放入的时间排序,而是会按照延迟的时间长短对任务进行排序,内部采用的是“堆”的数据结构,可以保证每次出队的任务都是当前队列中执行时间最靠前的。`DelayedWorkQueue` 添加元素满了之后会自动扩容,增加原来容量的 50%,即永远不会阻塞,最大扩容可达 `Integer.MAX_VALUE`,所以最多只能创建核心线程数的线程。
- `ArrayBlockingQueue`(有界阻塞队列):底层由数组实现,容量一旦创建,就不能修改。
### 线程池处理任务的流程了解吗?
### ⭐️线程池处理任务的流程了解吗?
![图解线程池实现原理](https://oss.javaguide.cn/github/javaguide/java/concurrent/thread-pool-principle.png)
@ -534,7 +546,7 @@ new RejectedExecutionHandler() {
- `prestartCoreThread()`:启动一个线程,等待任务,如果已达到核心线程数,这个方法返回 false否则返回 true
- `prestartAllCoreThreads()`:启动所有的核心线程,并返回启动成功的核心线程数。
### 线程池中线程异常后,销毁还是复用?
### ⭐️线程池中线程异常后,销毁还是复用?
直接说结论,需要分两种情况:
@ -547,7 +559,7 @@ new RejectedExecutionHandler() {
具体的源码分析可以参考这篇:[线程池中线程异常后:销毁还是复用? - 京东技术](https://mp.weixin.qq.com/s/9ODjdUU-EwQFF5PrnzOGfw)。
### 如何给线程池命名?
### ⭐️如何给线程池命名?
初始化线程池的时候需要显示命名(设置线程池名称前缀),有利于定位问题。
@ -634,7 +646,7 @@ CPU 密集型简单理解就是利用 CPU 计算能力的任务比如你在内
公式也只是参考,具体还是要根据项目实际线上运行情况来动态调整。我在后面介绍的美团的线程池参数动态配置这种方案就非常不错,很实用!
### 如何动态修改线程池的参数?
### ⭐️如何动态修改线程池的参数?
美团技术团队在[《Java 线程池实现原理及其在美团业务中的实践》](https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html)这篇文章中介绍到对线程池参数实现可自定义配置的思路和方法。
@ -660,14 +672,16 @@ CPU 密集型简单理解就是利用 CPU 计算能力的任务比如你在内
![动态配置线程池参数最终效果](https://oss.javaguide.cn/github/javaguide/java/concurrent/meituan-dynamically-configuring-thread-pool-parameters.png)
还没看够?推荐 why 神的[如何设置线程池参数?美团给出了一个让面试官虎躯一震的回答。](https://mp.weixin.qq.com/s/9HLuPcoWmTqAeFKa1kj-_A)这篇文章,深度剖析,很不错哦!
还没看够?我在[《后端面试高频系统设计&场景题》](https://javaguide.cn/zhuanlan/back-end-interview-high-frequency-system-design-and-scenario-questions.html#%E4%BB%8B%E7%BB%8D)中详细介绍了如何设计一个动态线程池,这也是面试中常问的一道系统设计题。
![《后端面试高频系统设计&场景题》](https://oss.javaguide.cn/xingqiu/back-end-interview-high-frequency-system-design-and-scenario-questions-fengmian.png)
如果我们的项目也想要实现这种效果的话,可以借助现成的开源项目:
- **[Hippo4j](https://github.com/opengoofy/hippo4j)**:异步线程池框架,支持线程池动态变更&监控&报警,无需修改代码轻松引入。支持多种使用模式,轻松引入,致力于提高系统运行保障能力。
- **[Dynamic TP](https://github.com/dromara/dynamic-tp)**:轻量级动态线程池,内置监控告警功能,集成三方中间件线程池管理,基于主流配置中心(已支持 Nacos、ApolloZookeeper、Consul、Etcd可通过 SPI 自定义实现)。
### 如何设计一个能够根据任务的优先级来执行的线程池?
### ⭐️如何设计一个能够根据任务的优先级来执行的线程池?
这是一个常见的面试问题,本质其实还是在考察求职者对于线程池以及阻塞队列的掌握。
@ -698,6 +712,10 @@ CPU 密集型简单理解就是利用 CPU 计算能力的任务比如你在内
## Future
重点是要掌握 `CompletableFuture` 的使用以及常见面试题。
除了下面的面试题之外,还推荐你看看我写的这篇文章: [CompletableFuture 详解](./completablefuture-intro.md)。
### Future 类有什么用?
`Future` 类是异步思想的典型运用,主要用在一些需要执行耗时任务的场景,避免程序一直原地等待耗时任务执行完成,执行效率太低。具体来说是这样的:当我们执行某一耗时的任务时,可以将这个耗时任务交给一个子线程去异步执行,同时我们可以干点其他事情,不用傻傻等待耗时任务执行完成。等我们的事情干完后,我们再通过 `Future` 类获取到耗时任务的执行结果。这样一来,程序的执行效率就明显提高了。
@ -789,6 +807,69 @@ public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
![](https://oss.javaguide.cn/javaguide/image-20210902093026059.png)
### ⭐️一个任务需要依赖另外两个任务执行完之后再执行,怎么设计?
这种任务编排场景非常适合通过`CompletableFuture`实现。这里假设要实现 T3 在 T2 和 T1 执行完后执行。
代码如下(这里为了简化代码,用到了 Hutool 的线程工具类 `ThreadUtil` 和日期时间工具类 `DateUtil`
```java
// T1
CompletableFuture<Void> futureT1 = CompletableFuture.runAsync(() -> {
System.out.println("T1 is executing. Current time" + DateUtil.now());
// 模拟耗时操作
ThreadUtil.sleep(1000);
});
// T2
CompletableFuture<Void> futureT2 = CompletableFuture.runAsync(() -> {
System.out.println("T2 is executing. Current time" + DateUtil.now());
ThreadUtil.sleep(1000);
});
// 使用allOf()方法合并T1和T2的CompletableFuture等待它们都完成
CompletableFuture<Void> bothCompleted = CompletableFuture.allOf(futureT1, futureT2);
// 当T1和T2都完成后执行T3
bothCompleted.thenRunAsync(() -> System.out.println("T3 is executing after T1 and T2 have completed.Current time" + DateUtil.now()));
// 等待所有任务完成,验证效果
ThreadUtil.sleep(3000);
```
通过 `CompletableFuture``allOf()`这个静态方法来并行运行 T1 和 T2 。当 T1 和
### ⭐️使用 CompletableFuture有一个任务失败如何处理异常
使用 `CompletableFuture`的时候一定要以正确的方式进行异常处理,避免异常丢失或者出现不可控问题。
下面是一些建议:
- 使用 `whenComplete` 方法可以在任务完成时触发回调函数,并正确地处理异常,而不是让异常被吞噬或丢失。
- 使用 `exceptionally` 方法可以处理异常并重新抛出,以便异常能够传播到后续阶段,而不是让异常被忽略或终止。
- 使用 `handle` 方法可以处理正常的返回结果和异常,并返回一个新的结果,而不是让异常影响正常的业务逻辑。
- 使用 `CompletableFuture.allOf` 方法可以组合多个 `CompletableFuture`,并统一处理所有任务的异常,而不是让异常处理过于冗长或重复。
- ……
### ⭐️在使用 CompletableFuture 的时候为什么要自定义线程池?
`CompletableFuture` 默认使用全局共享的 `ForkJoinPool.commonPool()` 作为执行器,所有未指定执行器的异步任务都会使用该线程池。这意味着应用程序、多个库或框架(如 Spring、第三方库若都依赖 `CompletableFuture`,默认情况下它们都会共享同一个线程池。
虽然 `ForkJoinPool` 效率很高,但当同时提交大量任务时,可能会导致资源竞争和线程饥饿,进而影响系统性能。
为避免这些问题,建议为 `CompletableFuture` 提供自定义线程池,带来以下优势:
- 隔离性:为不同任务分配独立的线程池,避免全局线程池资源争夺。
- 资源控制:根据任务特性调整线程池大小和队列类型,优化性能表现。
- 异常处理:通过自定义 `ThreadFactory` 更好地处理线程中的异常情况。
```java
private ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
CompletableFuture.runAsync(() -> {
//...
}, executor);
```
## AQS
### AQS 是什么?
@ -806,7 +887,7 @@ public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchron
AQS 为构建锁和同步器提供了一些通用功能的实现,因此,使用 AQS 能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的 `ReentrantLock``Semaphore`,其他的诸如 `ReentrantReadWriteLock``SynchronousQueue`等等皆是基于 AQS 的。
### AQS 的原理是什么?
### ⭐️AQS 的原理是什么?
AQS 核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制 AQS 是用 **CLH 队列锁** 实现的,即将暂时获取不到锁的线程加入到队列中。

View File

@ -5,6 +5,8 @@ tag:
- Java并发
---
<!-- markdownlint-disable MD024 -->
池化技术想必大家已经屡见不鲜了线程池、数据库连接池、HTTP 连接池等等都是对这个思想的应用。池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。
这篇文章我会详细介绍一下线程池的基本概念以及核心原理。

View File

@ -63,9 +63,9 @@ Java 源代码会经历 **编译器优化重排 —> 指令并行重排 —> 内
对于编译器优化重排和处理器的指令重排序(指令并行重排和内存系统重排都属于是处理器级别的指令重排序),处理该问题的方式不一样。
* 对于编译器,通过禁止特定类型的编译器重排序的方式来禁止重排序。
- 对于编译器,通过禁止特定类型的编译器重排序的方式来禁止重排序。
* 对于处理器通过插入内存屏障Memory Barrier或有时叫做内存栅栏Memory Fence的方式来禁止特定类型的处理器重排序。
- 对于处理器通过插入内存屏障Memory Barrier或有时叫做内存栅栏Memory Fence的方式来禁止特定类型的处理器重排序。
> 内存屏障Memory Barrier或有时叫做内存栅栏Memory Fence是一种 CPU 指令,用来禁止处理器指令发生重排序(像屏障一样),从而保障指令执行的有序性。另外,为了达到屏障的效果,它也会使处理器写入、读取值之前,将主内存的值写入高速缓存,清空无效队列,从而保障变量的可见性。

View File

@ -149,7 +149,7 @@ JVM 具有四种类型的 GC 实现:
-XX:+UseG1GC
```
有关*垃圾回收*实施的更多详细信息,请参见[此处](https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/jvm/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6.md)。
有关 _垃圾回收_ 实施的更多详细信息,请参见[此处](https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/jvm/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6.md)。
### 3.2.GC 日志记录

View File

@ -7,6 +7,8 @@ tag:
> 本文来自[cowbi](https://github.com/cowbi)的投稿~
<!-- markdownlint-disable MD024 -->
Oracle 于 2014 发布了 Java8jdk1.8),诸多原因使它成为目前市场上使用最多的 jdk 版本。虽然发布距今已将近 7 年,但很多程序员对其新特性还是不够了解,尤其是用惯了 Java8 之前版本的老程序员,比如我。
为了不脱离队伍太远,还是有必要对这些新特性做一些总结梳理。它较 jdk.7 有很多变化或者说是优化,比如 interface 里可以有静态方法,并且可以有方法体,这一点就颠覆了之前的认知;`java.util.HashMap` 数据结构里增加了红黑树;还有众所周知的 Lambda 表达式等等。本文不能把所有的新特性都给大家一一分享,只列出比较常用的新特性给大家做详细讲解。更多相关内容请看[官网关于 Java8 的新特性的介绍](https://www.oracle.com/java/technologies/javase/8-whats-new.html)。

View File

@ -366,8 +366,8 @@ DSA 算法签名过程:
## 参考
- 深入理解完美哈希 - 腾讯技术工程https://mp.weixin.qq.com/s/M8Wcj8sZ7UF1CMr887Puog
- 写给开发人员的实用密码学(二)—— 哈希函数https://thiscute.world/posts/practical-cryptography-basics-2-hash/
- 深入理解完美哈希 - 腾讯技术工程:<https://mp.weixin.qq.com/s/M8Wcj8sZ7UF1CMr887Puog>
- 写给开发人员的实用密码学(二)—— 哈希函数:<https://thiscute.world/posts/practical-cryptography-basics-2-hash/>
- 奇妙的安全旅行之 DSA 算法:<https://zhuanlan.zhihu.com/p/347025157>
- AES-GCM 加密简介:<https://juejin.cn/post/6844904122676690951>
- Java AES 256 GCM Encryption and Decryption Example | JCE Unlimited Strength<https://www.javainterviewpoint.com/java-aes-256-gcm-encryption-and-decryption/>

View File

@ -19,20 +19,20 @@
"**/*": "prettier --write --ignore-unknown",
".md": "markdownlint-cli2"
},
"packageManager": "pnpm@8.15.1",
"packageManager": "pnpm@9.11.0",
"dependencies": {
"@vuepress/bundler-vite": "2.0.0-rc.9",
"@vuepress/plugin-copyright": "2.0.0-rc.21",
"@vuepress/plugin-feed": "2.0.0-rc.21",
"@vuepress/plugin-search": "2.0.0-rc.21",
"husky": "9.0.10",
"markdownlint-cli2": "0.12.1",
"@vuepress/bundler-vite": "2.0.0-rc.15",
"@vuepress/plugin-feed": "2.0.0-rc.3",
"@vuepress/plugin-search": "2.0.0-rc.47",
"husky": "9.1.6",
"markdownlint-cli2": "0.14.0",
"mathjax-full": "3.2.2",
"nano-staged": "0.8.0",
"nodejs-jieba": "0.1.2",
"prettier": "3.2.5",
"vue": "^3.4.21",
"vuepress": "2.0.0-rc.9",
"vuepress-theme-hope": "2.0.0-rc.32"
"prettier": "3.3.3",
"sass-embedded": "1.79.3",
"vue": "^3.5.8",
"vuepress": "2.0.0-rc.15",
"vuepress-theme-hope": "2.0.0-rc.56"
}
}

2895
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff