mirror of
https://github.com/Snailclimb/JavaGuide
synced 2025-07-28 12:22:17 +08:00
Compare commits
7 Commits
74170ea66d
...
c800fc05bf
Author | SHA1 | Date | |
---|---|---|---|
|
c800fc05bf | ||
|
ad8458a23a | ||
|
e6b2c3c541 | ||
|
461d487519 | ||
|
e84117b8f3 | ||
|
510a8239c4 | ||
|
70819c92cb |
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@ -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
|
||||
|
@ -7,7 +7,7 @@ export default defineUserConfig({
|
||||
|
||||
title: "JavaGuide",
|
||||
description:
|
||||
"「Java学习指北 + Java面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识。准备 Java 面试,复习 Java 知识点,首选 JavaGuide! ",
|
||||
"「Java 学习指北 + Java 面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识。准备 Java 面试,复习 Java 知识点,首选 JavaGuide! ",
|
||||
lang: "zh-CN",
|
||||
|
||||
head: [
|
||||
@ -61,4 +61,5 @@ export default defineUserConfig({
|
||||
pagePatterns: ["**/*.md", "!**/*.snippet.md", "!.vuepress", "!node_modules"],
|
||||
|
||||
shouldPrefetch: false,
|
||||
shouldPreload: false,
|
||||
});
|
||||
|
1
docs/.vuepress/styles/config.scss
Normal file
1
docs/.vuepress/styles/config.scss
Normal file
@ -0,0 +1 @@
|
||||
$theme-color: #2980b9;
|
@ -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';
|
||||
|
@ -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("@"))
|
||||
|
70
docs/about-the-author/README.md
Normal file
70
docs/about-the-author/README.md
Normal file
@ -0,0 +1,70 @@
|
||||
---
|
||||
title: 个人介绍 Q&A
|
||||
category: 走近作者
|
||||
---
|
||||
|
||||
<!-- @include: @small-advertisement.snippet.md -->
|
||||
|
||||
这篇文章我会通过 Q&A 的形式简单介绍一下我自己。
|
||||
|
||||
## 我是什么时候毕业的?
|
||||
|
||||
很多老读者应该比较清楚,我是 19 年本科毕业的,刚毕业就去了某家外企“养老”。
|
||||
|
||||
我的学校背景是比较差的,高考失利,勉强过了一本线 20 来分,去了荆州的一所很普通的双非一本。不过,还好我没有因为学校而放弃自己,反倒是比身边的同学都要更努力,整个大学还算过的比较充实。
|
||||
|
||||
下面这张是当时拍的毕业照(后排最中间的就是我):
|
||||
|
||||

|
||||
|
||||
## 我坚持写了多久博客?
|
||||
|
||||
时间真快啊!我自己是从大二开始写博客的。那时候就是随意地在博客平台上发发自己的学习笔记和自己写的程序。就比如 [谢希仁老师的《计算机网络》内容总结](../cs-basics/network/computer-network-xiexiren-summary.md) 这篇文章就是我在大二学习计算机网络这门课的时候对照着教材总结的。
|
||||
|
||||
身边也有很多小伙伴经常问我:“我现在写博客还晚么?”
|
||||
|
||||
我觉得哈!如果你想做什么事情,尽量少问迟不迟,多问自己值不值得,只要你觉得有意义,就尽快开始做吧!人生很奇妙,我们每一步的重大决定,都会对自己未来的人生轨迹产生影响。是好还是坏,也只有我们自己知道了!
|
||||
|
||||
对我自己来说,坚持写博客这一项决定对我人生轨迹产生的影响是非常正面的!所以,我也推荐大家养成坚持写博客的习惯。
|
||||
|
||||
## 我在大学期间赚了多少钱?
|
||||
|
||||
在校期间,我还通过办培训班、接私活、技术培训、编程竞赛等方式变现 20w+,成功实现“经济独立”。我用自己赚的钱去了重庆、三亚、恩施、青岛等地旅游,还给家里补贴了很多,减轻了父母的负担。
|
||||
|
||||
下面这张是我大一下学期办补习班的时候拍的(离开前的最后一顿饭):
|
||||
|
||||

|
||||
|
||||
下面这张是我大三去三亚的时候拍的:
|
||||
|
||||

|
||||
|
||||
其实,我在大学就这么努力地开始赚钱,也主要是因为家庭条件太一般,父母赚钱都太辛苦了!也正是因为我自己迫切地想要减轻父母的负担,所以才会去尝试这么多赚钱的方法。
|
||||
|
||||
我发现做咱们程序员这行的,很多人的家庭条件都挺一般的,选择这个行业的很大原因不是因为自己喜欢,而是为了多赚点钱。
|
||||
|
||||
如果你也想通过接私活变现的话,可以在我的公众号后台回复“**接私活**”来了解一些我的个人经验分享。
|
||||
|
||||
::: center
|
||||
|
||||

|
||||
|
||||
:::
|
||||
|
||||
## 为什么自称 Guide?
|
||||
|
||||
可能是因为我的项目名字叫做 JavaGuide , 所以导致有很多人称呼我为 **Guide 哥**。
|
||||
|
||||
后面,为了读者更方便称呼,我就将自己的笔名改成了 **Guide**。
|
||||
|
||||
我早期写文章用的笔名是 SnailClimb 。很多人不知道这个名字是啥意思,给大家拆解一下就清楚了。SnailClimb=Snail(蜗牛)+Climb(攀登)。我从小就非常喜欢听周杰伦的歌曲,特别是他的《蜗牛》🐌 这首歌曲,另外,当年我高考发挥的算是比较失常,上了大学之后还算是比较“奋青”,所以,我就给自己起的笔名叫做 SnailClimb ,寓意自己要不断向上攀登,嘿嘿 😁
|
||||
|
||||

|
||||
|
||||
## 后记
|
||||
|
||||
凡心所向,素履所往,生如逆旅,一苇以航。
|
||||
|
||||
生活本就是有苦有甜。共勉!
|
||||
|
||||

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

|
||||
|
||||
:::
|
||||
|
||||
## 为什么自称 Guide?
|
||||
|
||||
|
@ -7,6 +7,8 @@ tag:
|
||||
|
||||
> 本文转自:<http://www.guoyaohua.com/sorting.html>,JavaGuide 对其做了补充完善。
|
||||
|
||||
<!-- markdownlint-disable MD024 -->
|
||||
|
||||
## 引言
|
||||
|
||||
所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。排序算法,就是如何使得记录按照要求排列的方法。排序算法在很多领域得到相当地重视,尤其是在大量数据的处理方面。一个优秀的算法可以节省大量的资源。在各个领域中考虑到数据的各种限制和规范,要得到一个符合实际的优秀算法,得经过大量的推理和分析。
|
||||
|
@ -5,6 +5,8 @@ tag:
|
||||
- 算法
|
||||
---
|
||||
|
||||
<!-- markdownlint-disable MD024 -->
|
||||
|
||||
## 1. 两数相加
|
||||
|
||||
### 题目描述
|
||||
|
@ -20,36 +20,36 @@ tag:
|
||||
3. **主机(host)**:连接在因特网上的计算机。
|
||||
4. **ISP(Internet Service Provider)**:因特网服务提供者(提供商)。
|
||||
|
||||

|
||||

|
||||
|
||||
5. **IXP(Internet eXchange Point)**:互联网交换点 IXP 的主要作用就是允许两个网络直接相连并交换分组,而不需要再通过第三个网络来转发分组。
|
||||
|
||||

|
||||

|
||||
|
||||
<p style="text-align:center;font-size:13px;color:gray">https://labs.ripe.net/Members/fergalc/ixp-traffic-during-stratos-skydive</p>
|
||||
<p style="text-align:center;font-size:13px;color:gray">https://labs.ripe.net/Members/fergalc/ixp-traffic-during-stratos-skydive</p>
|
||||
|
||||
6. **RFC(Request For Comments)**:意思是“请求评议”,包含了关于 Internet 几乎所有的重要的文字资料。
|
||||
7. **广域网 WAN(Wide Area Network)**:任务是通过长距离运送主机发送的数据。
|
||||
8. **城域网 MAN(Metropolitan Area Network)**:用来将多个局域网进行互连。
|
||||
9. **局域网 LAN(Local Area Network)**:学校或企业大多拥有多个互连的局域网。
|
||||
|
||||

|
||||

|
||||
|
||||
<p style="text-align:center;font-size:13px;color:gray">http://conexionesmanwman.blogspot.com/</p>
|
||||
<p style="text-align:center;font-size:13px;color:gray">http://conexionesmanwman.blogspot.com/</p>
|
||||
|
||||
10. **个人区域网 PAN(Personal Area Network)**:在个人工作的地方把属于个人使用的电子设备用无线技术连接起来的网络 。
|
||||
|
||||

|
||||

|
||||
|
||||
<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>
|
||||
<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 )**:路由器收到一个分组,先检查分组是否正确,并过滤掉冲突包错误。确定包正确后,取出目的地址,通过查找表找到想要发送的输出端口地址,然后将该包发送出去。
|
||||
|
||||

|
||||

|
||||
|
||||
14. **带宽(bandwidth)**:在计算机网络中,表示在单位时间内从网络中的某一点到另一点所能通过的“最高数据率”。常用来表示网络的通信线路所能传送数据的能力。单位是“比特每秒”,记为 b/s。
|
||||
15. **吞吐量(throughput )**:表示在单位时间内通过某个网络(或信道、接口)的数据量。吞吐量更经常地用于对现实世界中的网络的一种测量,以便知道实际上到底有多少数据量能够通过网络。吞吐量受网络的带宽或网络的额定速率的限制。
|
||||
13. **带宽(bandwidth)**:在计算机网络中,表示在单位时间内从网络中的某一点到另一点所能通过的“最高数据率”。常用来表示网络的通信线路所能传送数据的能力。单位是“比特每秒”,记为 b/s。
|
||||
14. **吞吐量(throughput )**:表示在单位时间内通过某个网络(或信道、接口)的数据量。吞吐量更经常地用于对现实世界中的网络的一种测量,以便知道实际上到底有多少数据量能够通过网络。吞吐量受网络的带宽或网络的额定速率的限制。
|
||||
|
||||
### 1.2. 重要知识点总结
|
||||
|
||||
@ -81,11 +81,11 @@ tag:
|
||||
5. **半双工(half duplex )**:通信的双方都可以发送信息,但不能双方同时发送(当然也就不能同时接收)。
|
||||
6. **全双工(full duplex)**:通信的双方可以同时发送和接收信息。
|
||||
|
||||

|
||||

|
||||
|
||||
7. **失真**:失去真实性,主要是指接受到的信号和发送的信号不同,有磨损和衰减。影响失真程度的因素:1.码元传输速率 2.信号传输距离 3.噪声干扰 4.传输媒体质量
|
||||
|
||||

|
||||

|
||||
|
||||
8. **奈氏准则**:在任何信道中,码元的传输的效率是有上限的,传输速率超过此上限,就会出现严重的码间串扰问题,使接收端对码元的判决(即识别)成为不可能。
|
||||
9. **香农定理**:在带宽受限且有噪声的信道中,为了不产生误差,信息的数据传输速率有上限值。
|
||||
@ -95,7 +95,7 @@ tag:
|
||||
13. **信噪比(signal-to-noise ratio )**:指信号的平均功率和噪声的平均功率之比,记为 S/N。信噪比(dB)=10\*log10(S/N)。
|
||||
14. **信道复用(channel multiplexing )**:指多个用户共享同一个信道。(并不一定是同时)。
|
||||
|
||||

|
||||

|
||||
|
||||
15. **比特率(bit rate )**:单位时间(每秒)内传送的比特数。
|
||||
16. **波特率(baud rate)**:单位时间载波调制状态改变的次数。针对数据信号对载波的调制速率。
|
||||
@ -151,7 +151,7 @@ tag:
|
||||

|
||||
8. **MAC 地址(Media Access Control 或者 Medium Access Control)**:意译为媒体访问控制,或称为物理地址、硬件地址,用来定义网络设备的位置。在 OSI 模型中,第三层网络层负责 IP 地址,第二层数据链路层则负责 MAC 地址。因此一个主机会有一个 MAC 地址,而每个网络位置会有一个专属于它的 IP 地址 。地址是识别某个系统的重要标识符,“名字指出我们所要寻找的资源,地址指出资源所在的地方,路由告诉我们如何到达该处。”
|
||||
|
||||

|
||||

|
||||
|
||||
9. **网桥(bridge)**:一种用于数据链路层实现中继,连接两个或多个局域网的网络互连设备。
|
||||
10. **交换机(switch )**:广义的来说,交换机指的是一种通信系统中完成信息交换的设备。这里工作在数据链路层的交换机指的是交换式集线器,其实质是一个多接口的网桥
|
||||
@ -218,7 +218,7 @@ tag:
|
||||
4. **TCP(Transmission Control Protocol)**:传输控制协议。
|
||||
5. **UDP(User Datagram Protocol)**:用户数据报协议。
|
||||
|
||||

|
||||

|
||||
|
||||
6. **端口(port)**:端口的目的是为了确认对方机器的哪个进程在与自己进行交互,比如 MSN 和 QQ 的端口不同,如果没有端口就可能出现 QQ 进程和 MSN 交互错误。端口又称协议端口号。
|
||||
7. **停止等待协议(stop-and-wait)**:指发送方每发送完一个分组就停止发送,等待对方确认,在收到确认之后在发送下一个分组。
|
||||
@ -267,34 +267,34 @@ tag:
|
||||
|
||||
1. **域名系统(DNS)**:域名系统(DNS,Domain Name System)将人类可读的域名 (例如,www.baidu.com) 转换为机器可读的 IP 地址 (例如,220.181.38.148)。我们可以将其理解为专为互联网设计的电话薄。
|
||||
|
||||

|
||||

|
||||
|
||||
<p style="text-align:right;font-size:12px">https://www.seobility.net/en/wiki/HTTP_headers</p>
|
||||
<p style="text-align:right;font-size:12px">https://www.seobility.net/en/wiki/HTTP_headers</p>
|
||||
|
||||
2. **文件传输协议(FTP)**:FTP 是 File Transfer Protocol(文件传输协议)的英文简称,而中文简称为“文传协议”。用于 Internet 上的控制文件的双向传输。同时,它也是一个应用程序(Application)。基于不同的操作系统有不同的 FTP 应用程序,而所有这些应用程序都遵守同一种协议以传输文件。在 FTP 的使用当中,用户经常遇到两个概念:"下载"(Download)和"上传"(Upload)。 "下载"文件就是从远程主机拷贝文件至自己的计算机上;"上传"文件就是将文件从自己的计算机中拷贝至远程主机上。用 Internet 语言来说,用户可通过客户机程序向(从)远程主机上传(下载)文件。
|
||||
|
||||

|
||||

|
||||
|
||||
3. **简单文件传输协议(TFTP)**:TFTP(Trivial File Transfer Protocol,简单文件传输协议)是 TCP/IP 协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议,提供不复杂、开销不大的文件传输服务。端口号为 69。
|
||||
4. **远程终端协议(TELNET)**:Telnet 协议是 TCP/IP 协议族中的一员,是 Internet 远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。在终端使用者的电脑上使用 telnet 程序,用它连接到服务器。终端使用者可以在 telnet 程序中输入命令,这些命令会在服务器上运行,就像直接在服务器的控制台上输入一样。可以在本地就能控制服务器。要开始一个 telnet 会话,必须输入用户名和密码来登录服务器。Telnet 是常用的远程控制 Web 服务器的方法。
|
||||
5. **万维网(WWW)**:WWW 是环球信息网的缩写,(亦作“Web”、“WWW”、“'W3'”,英文全称为“World Wide Web”),中文名字为“万维网”,"环球网"等,常简称为 Web。分为 Web 客户端和 Web 服务器程序。WWW 可以让 Web 客户端(常用浏览器)访问浏览 Web 服务器上的页面。是一个由许多互相链接的超文本组成的系统,通过互联网访问。在这个系统中,每个有用的事物,称为一样“资源”;并且由一个全局“统一资源标识符”(URI)标识;这些资源通过超文本传输协议(Hypertext Transfer Protocol)传送给用户,而后者通过点击链接来获得资源。万维网联盟(英语:World Wide Web Consortium,简称 W3C),又称 W3C 理事会。1994 年 10 月在麻省理工学院(MIT)计算机科学实验室成立。万维网联盟的创建者是万维网的发明者蒂姆·伯纳斯-李。万维网并不等同互联网,万维网只是互联网所能提供的服务其中之一,是靠着互联网运行的一项服务。
|
||||
6. **万维网的大致工作工程:**
|
||||
|
||||

|
||||

|
||||
|
||||
7. **统一资源定位符(URL)**:统一资源定位符是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的 URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
|
||||
8. **超文本传输协议(HTTP)**:超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。所有的 WWW 文件都必须遵守这个标准。设计 HTTP 最初的目的是为了提供一种发布和接收 HTML 页面的方法。1960 年美国人 Ted Nelson 构思了一种通过计算机处理文本信息的方法,并称之为超文本(hypertext),这成为了 HTTP 超文本传输协议标准架构的发展根基。
|
||||
|
||||
HTTP 协议的本质就是一种浏览器与服务器之间约定好的通信格式。HTTP 的原理如下图所示:
|
||||
HTTP 协议的本质就是一种浏览器与服务器之间约定好的通信格式。HTTP 的原理如下图所示:
|
||||
|
||||

|
||||

|
||||
|
||||
10. **代理服务器(Proxy Server)**:代理服务器(Proxy Server)是一种网络实体,它又称为万维网高速缓存。 代理服务器把最近的一些请求和响应暂存在本地磁盘中。当新请求到达时,若代理服务器发现这个请求与暂时存放的的请求相同,就返回暂存的响应,而不需要按 URL 的地址再次去互联网访问该资源。代理服务器可在客户端或服务器工作,也可以在中间系统工作。
|
||||
11. **简单邮件传输协议(SMTP)** : SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。 SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。 通过 SMTP 协议所指定的服务器,就可以把 E-mail 寄到收信人的服务器上了,整个过程只要几分钟。SMTP 服务器则是遵循 SMTP 协议的发送邮件服务器,用来发送或中转发出的电子邮件。
|
||||
9. **代理服务器(Proxy Server)**:代理服务器(Proxy Server)是一种网络实体,它又称为万维网高速缓存。 代理服务器把最近的一些请求和响应暂存在本地磁盘中。当新请求到达时,若代理服务器发现这个请求与暂时存放的的请求相同,就返回暂存的响应,而不需要按 URL 的地址再次去互联网访问该资源。代理服务器可在客户端或服务器工作,也可以在中间系统工作。
|
||||
10. **简单邮件传输协议(SMTP)** : SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。 SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。 通过 SMTP 协议所指定的服务器,就可以把 E-mail 寄到收信人的服务器上了,整个过程只要几分钟。SMTP 服务器则是遵循 SMTP 协议的发送邮件服务器,用来发送或中转发出的电子邮件。
|
||||
|
||||

|
||||

|
||||
|
||||
<p style="text-align:right;font-size:12px">https://www.campaignmonitor.com/resources/knowledge-base/what-is-the-code-that-makes-bcc-or-cc-operate-in-an-email/</p>
|
||||
<p style="text-align:right;font-size:12px">https://www.campaignmonitor.com/resources/knowledge-base/what-is-the-code-that-makes-bcc-or-cc-operate-in-an-email/</p>
|
||||
|
||||
11. **搜索引擎** :搜索引擎(Search Engine)是指根据一定的策略、运用特定的计算机程序从互联网上搜集信息,在对信息进行组织和处理后,为用户提供检索服务,将用户检索相关的信息展示给用户的系统。搜索引擎包括全文索引、目录索引、元搜索引擎、垂直搜索引擎、集合式搜索引擎、门户搜索引擎与免费链接列表等。
|
||||
|
||||
|
@ -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 -->
|
||||
|
@ -132,40 +132,40 @@ 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 |
|
||||
| Accept-Encoding | 能够接受的编码方式列表。参考 HTTP 压缩。 | Accept-Encoding: gzip, deflate |
|
||||
| Accept-Language | 能够接受的回应内容的自然语言列表。 | Accept-Language: en-US |
|
||||
| Authorization | 用于超文本传输协议的认证的认证信息 | Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== |
|
||||
| Cache-Control | 用来指定在这次的请求/响应链中的所有缓存机制 都必须 遵守的指令 | Cache-Control: no-cache |
|
||||
| Connection | 该浏览器想要优先使用的连接类型 | Connection: keep-alive |
|
||||
| Content-Length | 以八位字节数组(8 位的字节)表示的请求体的长度 | Content-Length: 348 |
|
||||
| Content-MD5 | 请求体的内容的二进制 MD5 散列值,以 Base64 编码的结果 | Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ== |
|
||||
| Content-Type | 请求体的多媒体类型(用于 POST 和 PUT 请求中) | Content-Type: application/x-www-form-urlencoded |
|
||||
| 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) |
|
||||
| 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 |
|
||||
| If-None-Match | 允许服务器在请求的资源的 ETag 未发生变化的情况下返回 `304 Not Modified` 状态码 | If-None-Match: "737060cd8c284d8af7ad3082f209582d" |
|
||||
| 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/) |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| Via | 向服务器告知,这个请求是由哪些代理发出的。 | Via: 1.0 fred, 1.1 example.com (Apache/1.1) |
|
||||
| Warning | 一个一般性的警告,告知,在实体内容体中可能存在错误。 | Warning: 199 Miscellaneous warning |
|
||||
| 请求头字段名 | 说明 | 示例 |
|
||||
| :------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------- |
|
||||
| Accept | 能够接受的回应内容类型(Content-Types)。 | Accept: text/plain |
|
||||
| Accept-Charset | 能够接受的字符集 | Accept-Charset: utf-8 |
|
||||
| Accept-Datetime | 能够接受的按照时间来表示的版本 | Accept-Datetime: Thu, 31 May 2007 20:35:00 GMT |
|
||||
| Accept-Encoding | 能够接受的编码方式列表。参考 HTTP 压缩。 | Accept-Encoding: gzip, deflate |
|
||||
| Accept-Language | 能够接受的回应内容的自然语言列表。 | Accept-Language: en-US |
|
||||
| Authorization | 用于超文本传输协议的认证的认证信息 | Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== |
|
||||
| Cache-Control | 用来指定在这次的请求/响应链中的所有缓存机制 都必须 遵守的指令 | Cache-Control: no-cache |
|
||||
| Connection | 该浏览器想要优先使用的连接类型 | Connection: keep-alive |
|
||||
| Content-Length | 以八位字节数组(8 位的字节)表示的请求体的长度 | Content-Length: 348 |
|
||||
| Content-MD5 | 请求体的内容的二进制 MD5 散列值,以 Base64 编码的结果 | Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ== |
|
||||
| Content-Type | 请求体的多媒体类型(用于 POST 和 PUT 请求中) | Content-Type: application/x-www-form-urlencoded |
|
||||
| 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` |
|
||||
| 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 |
|
||||
| If-None-Match | 允许服务器在请求的资源的 ETag 未发生变化的情况下返回 `304 Not Modified` 状态码 | If-None-Match: "737060cd8c284d8af7ad3082f209582d" |
|
||||
| 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` |
|
||||
| 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` |
|
||||
| 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 |
|
||||
| Via | 向服务器告知,这个请求是由哪些代理发出的。 | Via: 1.0 fred, 1.1 example.com (Apache/1.1) |
|
||||
| Warning | 一个一般性的警告,告知,在实体内容体中可能存在错误。 | Warning: 199 Miscellaneous warning |
|
||||
|
||||
### HTTP 和 HTTPS 有什么区别?(重要)
|
||||
|
||||
|
@ -606,7 +606,7 @@ InnoDB 的行锁是针对索引字段加的锁,表级锁是针对非索引字
|
||||
|
||||
InnoDB 行锁是通过对索引数据页上的记录加锁实现的,MySQL InnoDB 支持三种行锁定方式:
|
||||
|
||||
- **记录锁(Record Lock)**:也被称为记录锁,属于单个行记录上的锁。
|
||||
- **记录锁(Record Lock)**:属于单个行记录上的锁。
|
||||
- **间隙锁(Gap Lock)**:锁定一个范围,不包括记录本身。
|
||||
- **临键锁(Next-Key Lock)**:Record Lock+Gap Lock,锁定一个范围,包含记录本身,主要目的是为了解决幻读问题(MySQL 事务部分提到过)。记录锁只能锁住已经存在的记录,为了避免插入新记录,需要依赖间隙锁。
|
||||
|
||||
|
@ -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
|
||||
@ -78,7 +78,7 @@ zset-max-ziplist-entries 128
|
||||
|
||||
为了更好的回答上述问题以及更好的理解和掌握跳表,这里可以通过手写一个简单的跳表的形式来帮助读者理解跳表这个数据结构。
|
||||
|
||||
我们都知道有序链表在添加、查询、删除的平均时间复杂都都是**O(n)**即线性增长,所以一旦节点数量达到一定体量后其性能表现就会非常差劲。而跳表我们完全可以理解为在原始链表基础上,建立多级索引,通过多级索引检索定位将增删改查的时间复杂度变为**O(log n)**。
|
||||
我们都知道有序链表在添加、查询、删除的平均时间复杂都都是 **O(n)** 即线性增长,所以一旦节点数量达到一定体量后其性能表现就会非常差劲。而跳表我们完全可以理解为在原始链表基础上,建立多级索引,通过多级索引检索定位将增删改查的时间复杂度变为 **O(log n)** 。
|
||||
|
||||
可能这里说的有些抽象,我们举个例子,以下图跳表为例,其原始链表存储按序存储 1-10,有 2 级索引,每级索引的索引个数都是基于下层元素个数的一半。
|
||||
|
||||
@ -145,8 +145,8 @@ r=n/2^k
|
||||
1. 跳表的高度计算从原始链表开始,即默认情况下插入的元素的高度为 1,代表没有索引,只有元素节点。
|
||||
2. 设计一个为插入元素生成节点索引高度 level 的方法。
|
||||
3. 进行一次随机运算,随机数值范围为 0-1 之间。
|
||||
4. 如果随机数大于 0.5 则为当前元素添加一级索引,自此我们保证生成一级索引的概率为**50%**,这也就保证了 1 级索引理想情况下只有一半的元素会生成索引。
|
||||
5. 同理后续每次随机算法得到的值大于 0.5 时,我们的索引高度就加 1,这样就可以保证节点生成的 2 级索引概率为**25%**,3 级索引为**12.5%**……
|
||||
4. 如果随机数大于 0.5 则为当前元素添加一级索引,自此我们保证生成一级索引的概率为 **50%** ,这也就保证了 1 级索引理想情况下只有一半的元素会生成索引。
|
||||
5. 同理后续每次随机算法得到的值大于 0.5 时,我们的索引高度就加 1,这样就可以保证节点生成的 2 级索引概率为 **25%** ,3 级索引为 **12.5%** ……
|
||||
|
||||
我们回过头,上述插入 7 之后,我们通过随机算法得到 2,即要为其建立 1 级索引:
|
||||
|
||||
@ -283,10 +283,10 @@ public void add(int value) {
|
||||
|
||||
查询逻辑比较简单,从跳表最高级的索引开始定位找到小于要查的 value 的最大值,以下图为例,我们希望查找到节点 8:
|
||||
|
||||
1. 跳表的 3 级索引首先找找到 5 的索引,5 的 3 级索引**forwards[3]**指向空,索引直接向下。
|
||||
2. 来到 5 的 2 级索引,其后继**forwards[2]**指向 8,继续向下。
|
||||
3. 5 的 1 级索引**forwards[1]**指向索引 6,继续向前。
|
||||
4. 索引 6 的**forwards[1]**指向索引 8,继续向下。
|
||||
1. 跳表的 3 级索引首先找找到 5 的索引,5 的 3 级索引 **forwards[3]** 指向空,索引直接向下。
|
||||
2. 来到 5 的 2 级索引,其后继 **forwards[2]** 指向 8,继续向下。
|
||||
3. 5 的 1 级索引 **forwards[1]** 指向索引 6,继续向前。
|
||||
4. 索引 6 的 **forwards[1]** 指向索引 8,继续向下。
|
||||
5. 我们在原始节点向前找到节点 7。
|
||||
6. 节点 7 后续就是节点 8,继续向前为节点 8,无法继续向下,结束搜寻。
|
||||
7. 判断 7 的前驱,等于 8,查找结束。
|
||||
@ -605,7 +605,7 @@ Node{data=23, maxLevel=1}
|
||||
|
||||
### 平衡树 vs 跳表
|
||||
|
||||
先来说说它和平衡树的比较,平衡树我们又会称之为 **AVL 树**,是一个严格的平衡二叉树,平衡条件必须满足(所有节点的左右子树高度差不超过 1,即平衡因子为范围为 `[-1,1]`)。平衡树的插入、删除和查询的时间复杂度和跳表一样都是 **O(log n)**。
|
||||
先来说说它和平衡树的比较,平衡树我们又会称之为 **AVL 树**,是一个严格的平衡二叉树,平衡条件必须满足(所有节点的左右子树高度差不超过 1,即平衡因子为范围为 `[-1,1]`)。平衡树的插入、删除和查询的时间复杂度和跳表一样都是 **O(log n)** 。
|
||||
|
||||
对于范围查询来说,它也可以通过中序遍历的方式达到和跳表一样的效果。但是它的每一次插入或者删除操作都需要保证整颗树左右节点的绝对平衡,只要不平衡就要通过旋转操作来保持平衡,这个过程是比较耗时的。
|
||||
|
||||
@ -674,7 +674,7 @@ private Node add(Node node, K key, V value) {
|
||||
|
||||
### 红黑树 vs 跳表
|
||||
|
||||
红黑树(Red Black Tree)也是一种自平衡二叉查找树,它的查询性能略微逊色于 AVL 树,但插入和删除效率更高。红黑树的插入、删除和查询的时间复杂度和跳表一样都是 **O(log n)**。
|
||||
红黑树(Red Black Tree)也是一种自平衡二叉查找树,它的查询性能略微逊色于 AVL 树,但插入和删除效率更高。红黑树的插入、删除和查询的时间复杂度和跳表一样都是 **O(log n)** 。
|
||||
|
||||
红黑树是一个**黑平衡树**,即从任意节点到另外一个叶子叶子节点,它所经过的黑节点是一样的。当对它进行插入操作时,需要通过旋转和染色(红黑变换)来保证黑平衡。不过,相较于 AVL 树为了维持平衡的开销要小一些。关于红黑树的详细介绍,可以查看这篇文章:[红黑树](https://javaguide.cn/cs-basics/data-structure/red-black-tree.html)。
|
||||
|
||||
@ -726,7 +726,7 @@ private Node < K, V > add(Node < K, V > node, K key, V val) {
|
||||
|
||||
1. **多叉树结构**:它是一棵多叉树,每个节点可以包含多个子节点,减小了树的高度,查询效率高。
|
||||
2. **存储效率高**:其中非叶子节点存储多个 key,叶子节点存储 value,使得每个节点更够存储更多的键,根据索引进行范围查询时查询效率更高。-
|
||||
3. **平衡性**:它是绝对的平衡,即树的各个分支高度相差不大,确保查询和插入时间复杂度为**O(log n)**。
|
||||
3. **平衡性**:它是绝对的平衡,即树的各个分支高度相差不大,确保查询和插入时间复杂度为 **O(log n)** 。
|
||||
4. **顺序访问**:叶子节点间通过链表指针相连,范围查询表现出色。
|
||||
5. **数据均匀分布**:B+树插入时可能会导致数据重新分布,使得数据在整棵树分布更加均匀,保证范围查询和删除效率。
|
||||
|
||||
|
@ -430,7 +430,7 @@ SPI 将服务接口和具体的服务实现分离开来,将服务调用方和
|
||||
一般模块之间都是通过接口进行通讯,因此我们在服务调用方和服务实现方(也称服务提供者)之间引入一个“接口”。
|
||||
|
||||
- 当实现方提供了接口和实现,我们可以通过调用实现方的接口从而拥有实现方给我们提供的能力,这就是 **API**。这种情况下,接口和实现都是放在实现方的包中。调用方通过接口调用实现方的功能,而不需要关心具体的实现细节。
|
||||
- 当接口存在于调用方这边时,这就是 **SPI ** 。由接口调用方确定接口规则,然后由不同的厂商根据这个规则对这个接口进行实现,从而提供服务。
|
||||
- 当接口存在于调用方这边时,这就是 **SPI** 。由接口调用方确定接口规则,然后由不同的厂商根据这个规则对这个接口进行实现,从而提供服务。
|
||||
|
||||
举个通俗易懂的例子:公司 H 是一家科技公司,新设计了一款芯片,然后现在需要量产了,而市面上有好几家芯片制造业公司,这个时候,只要 H 公司指定好了这芯片生产的标准(定义好了接口标准),那么这些合作的芯片公司(服务提供者)就按照标准交付自家特色的芯片(提供不同方案的实现,但是给出来的结果是一样的)。
|
||||
|
||||
|
@ -41,7 +41,7 @@ SPI 将服务接口和具体的服务实现分离开来,将服务调用方和
|
||||
一般模块之间都是通过接口进行通讯,因此我们在服务调用方和服务实现方(也称服务提供者)之间引入一个“接口”。
|
||||
|
||||
- 当实现方提供了接口和实现,我们可以通过调用实现方的接口从而拥有实现方给我们提供的能力,这就是 **API**。这种情况下,接口和实现都是放在实现方的包中。调用方通过接口调用实现方的功能,而不需要关心具体的实现细节。
|
||||
- 当接口存在于调用方这边时,这就是 **SPI ** 。由接口调用方确定接口规则,然后由不同的厂商根据这个规则对这个接口进行实现,从而提供服务。
|
||||
- 当接口存在于调用方这边时,这就是 **SPI** 。由接口调用方确定接口规则,然后由不同的厂商根据这个规则对这个接口进行实现,从而提供服务。
|
||||
|
||||
举个通俗易懂的例子:公司 H 是一家科技公司,新设计了一款芯片,然后现在需要量产了,而市面上有好几家芯片制造业公司,这个时候,只要 H 公司指定好了这芯片生产的标准(定义好了接口标准),那么这些合作的芯片公司(服务提供者)就按照标准交付自家特色的芯片(提供不同方案的实现,但是给出来的结果是一样的)。
|
||||
|
||||
|
@ -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` 的类。
|
||||
|
||||
那这个类主要是用来干什么的呢?有什么使用场景呢?这篇文章就带你搞清楚!
|
||||
|
@ -14,6 +14,8 @@ head:
|
||||
|
||||
<!-- @include: @small-advertisement.snippet.md -->
|
||||
|
||||
<!-- markdownlint-disable MD024 -->
|
||||
|
||||
## 集合概述
|
||||
|
||||
### Java 集合概览
|
||||
|
@ -5,6 +5,8 @@ tag:
|
||||
- Java并发
|
||||
---
|
||||
|
||||
<!-- markdownlint-disable MD024 -->
|
||||
|
||||
## AQS 介绍
|
||||
|
||||
AQS 的全称为 `AbstractQueuedSynchronizer` ,翻译过来的意思就是抽象队列同步器。这个类在 `java.util.concurrent.locks` 包下面。
|
||||
|
@ -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 本身不负责
|
||||
- 线程安全指的是在多线程环境下,对于同一份数据,不管有多少个线程同时访问,都能保证这份数据的正确性和一致性。
|
||||
- 线程不安全则表示在多线程环境下,对于同一份数据,多个线程同时访问时可能会导致数据混乱、错误或者丢失。
|
||||
|
||||
## 死锁
|
||||
## ⭐️死锁
|
||||
|
||||
### 什么是线程死锁?
|
||||
|
||||
|
@ -14,11 +14,11 @@ head:
|
||||
|
||||
<!-- @include: @article-header.snippet.md -->
|
||||
|
||||
## JMM(Java 内存模型)
|
||||
## ⭐️JMM(Java 内存模型)
|
||||
|
||||
JMM(Java 内存模型)相关的问题比较多,也比较重要,于是我单独抽了一篇文章来总结 JMM 相关的知识点和问题:[JMM(Java 内存模型)详解](./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 有什么区别?
|
||||
|
||||
#### 两者都是可重入锁
|
||||
|
||||
|
@ -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 内存泄露问题是怎么导致的?
|
||||
### ⭐️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`(有界阻塞队列):底层由数组实现,容量一旦创建,就不能修改。
|
||||
|
||||
### 线程池处理任务的流程了解吗?
|
||||
### ⭐️线程池处理任务的流程了解吗?
|
||||
|
||||

|
||||
|
||||
@ -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 计算能力的任务比如你在内
|
||||
|
||||

|
||||
|
||||
还没看够?推荐 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)中详细介绍了如何设计一个动态线程池,这也是面试中常问的一道系统设计题。
|
||||
|
||||

|
||||
|
||||
如果我们的项目也想要实现这种效果的话,可以借助现成的开源项目:
|
||||
|
||||
- **[Hippo4j](https://github.com/opengoofy/hippo4j)**:异步线程池框架,支持线程池动态变更&监控&报警,无需修改代码轻松引入。支持多种使用模式,轻松引入,致力于提高系统运行保障能力。
|
||||
- **[Dynamic TP](https://github.com/dromara/dynamic-tp)**:轻量级动态线程池,内置监控告警功能,集成三方中间件线程池管理,基于主流配置中心(已支持 Nacos、Apollo,Zookeeper、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> {
|
||||
|
||||

|
||||
|
||||
### ⭐️一个任务需要依赖另外两个任务执行完之后再执行,怎么设计?
|
||||
|
||||
这种任务编排场景非常适合通过`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 队列锁** 实现的,即将暂时获取不到锁的线程加入到队列中。
|
||||
|
||||
|
@ -5,6 +5,8 @@ tag:
|
||||
- Java并发
|
||||
---
|
||||
|
||||
<!-- markdownlint-disable MD024 -->
|
||||
|
||||
池化技术想必大家已经屡见不鲜了,线程池、数据库连接池、HTTP 连接池等等都是对这个思想的应用。池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。
|
||||
|
||||
这篇文章我会详细介绍一下线程池的基本概念以及核心原理。
|
||||
|
@ -63,9 +63,9 @@ Java 源代码会经历 **编译器优化重排 —> 指令并行重排 —> 内
|
||||
|
||||
对于编译器优化重排和处理器的指令重排序(指令并行重排和内存系统重排都属于是处理器级别的指令重排序),处理该问题的方式不一样。
|
||||
|
||||
* 对于编译器,通过禁止特定类型的编译器重排序的方式来禁止重排序。
|
||||
- 对于编译器,通过禁止特定类型的编译器重排序的方式来禁止重排序。
|
||||
|
||||
* 对于处理器,通过插入内存屏障(Memory Barrier,或有时叫做内存栅栏,Memory Fence)的方式来禁止特定类型的处理器重排序。
|
||||
- 对于处理器,通过插入内存屏障(Memory Barrier,或有时叫做内存栅栏,Memory Fence)的方式来禁止特定类型的处理器重排序。
|
||||
|
||||
> 内存屏障(Memory Barrier,或有时叫做内存栅栏,Memory Fence)是一种 CPU 指令,用来禁止处理器指令发生重排序(像屏障一样),从而保障指令执行的有序性。另外,为了达到屏障的效果,它也会使处理器写入、读取值之前,将主内存的值写入高速缓存,清空无效队列,从而保障变量的可见性。
|
||||
|
||||
|
@ -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 日志记录
|
||||
|
||||
|
@ -7,6 +7,8 @@ tag:
|
||||
|
||||
> 本文来自[cowbi](https://github.com/cowbi)的投稿~
|
||||
|
||||
<!-- markdownlint-disable MD024 -->
|
||||
|
||||
Oracle 于 2014 发布了 Java8(jdk1.8),诸多原因使它成为目前市场上使用最多的 jdk 版本。虽然发布距今已将近 7 年,但很多程序员对其新特性还是不够了解,尤其是用惯了 Java8 之前版本的老程序员,比如我。
|
||||
|
||||
为了不脱离队伍太远,还是有必要对这些新特性做一些总结梳理。它较 jdk.7 有很多变化或者说是优化,比如 interface 里可以有静态方法,并且可以有方法体,这一点就颠覆了之前的认知;`java.util.HashMap` 数据结构里增加了红黑树;还有众所周知的 Lambda 表达式等等。本文不能把所有的新特性都给大家一一分享,只列出比较常用的新特性给大家做详细讲解。更多相关内容请看[官网关于 Java8 的新特性的介绍](https://www.oracle.com/java/technologies/javase/8-whats-new.html)。
|
||||
|
@ -346,7 +346,7 @@ Spring 框架中用到了哪些设计模式?
|
||||
|
||||
- 《Spring 技术内幕》
|
||||
- <https://blog.eduonix.com/java-programming-2/learn-design-patterns-used-spring-framework/>
|
||||
- <http://blog.yeamin.top/2018/03/27/单例模式-Spring%20 单例实现原理分析/>
|
||||
- <http://blog.yeamin.top/2018/03/27/单例模式-Spring%20单例实现原理分析/>
|
||||
- <https://www.tutorialsteacher.com/ioc/inversion-of-control>
|
||||
- <https://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/observer.html>
|
||||
- <https://juejin.im/post/5a8eb261f265da4e9e307230>
|
||||
|
@ -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/>
|
||||
|
22
package.json
22
package.json
@ -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
2895
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user