mirror of
https://github.com/Snailclimb/JavaGuide
synced 2025-07-28 12:22:17 +08:00
Compare commits
8 Commits
53ce8e9d14
...
0f9d4cc108
Author | SHA1 | Date | |
---|---|---|---|
|
0f9d4cc108 | ||
|
6d35f83a6b | ||
|
1003ddf9a3 | ||
|
0abed38dfe | ||
|
1549b4c33a | ||
|
cc768eb6dd | ||
|
b286e7fb11 | ||
|
c7e708581a |
7
.github/workflows/test.yml
vendored
7
.github/workflows/test.yml
vendored
@ -14,15 +14,16 @@ jobs:
|
|||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
with:
|
|
||||||
run_install: true
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20.17.0
|
node-version: 22
|
||||||
cache: pnpm
|
cache: pnpm
|
||||||
|
|
||||||
|
- name: Install deps
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build test
|
- name: Build test
|
||||||
env:
|
env:
|
||||||
NODE_OPTIONS: --max_old_space_size=4096
|
NODE_OPTIONS: --max_old_space_size=4096
|
||||||
|
@ -41,6 +41,25 @@ export default hopeTheme({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
markdown: {
|
||||||
|
align: true,
|
||||||
|
codeTabs: true,
|
||||||
|
gfm: true,
|
||||||
|
include: {
|
||||||
|
resolvePath: (file, cwd) => {
|
||||||
|
if (file.startsWith("@"))
|
||||||
|
return path.resolve(
|
||||||
|
__dirname,
|
||||||
|
"../snippets",
|
||||||
|
file.replace("@", "./"),
|
||||||
|
);
|
||||||
|
|
||||||
|
return path.resolve(cwd, file);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tasklist: true,
|
||||||
|
},
|
||||||
|
|
||||||
plugins: {
|
plugins: {
|
||||||
blog: true,
|
blog: true,
|
||||||
|
|
||||||
@ -59,28 +78,6 @@ export default hopeTheme({
|
|||||||
rss: true,
|
rss: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
markdownTab: {
|
|
||||||
codeTabs: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
mdEnhance: {
|
|
||||||
align: true,
|
|
||||||
gfm: true,
|
|
||||||
include: {
|
|
||||||
resolvePath: (file, cwd) => {
|
|
||||||
if (file.startsWith("@"))
|
|
||||||
return path.resolve(
|
|
||||||
__dirname,
|
|
||||||
"../snippets",
|
|
||||||
file.replace("@", "./"),
|
|
||||||
);
|
|
||||||
|
|
||||||
return path.resolve(cwd, file);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
tasklist: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
search: {
|
search: {
|
||||||
isSearchable: (page) => page.path !== "/",
|
isSearchable: (page) => page.path !== "/",
|
||||||
maxSuggestions: 10,
|
maxSuggestions: 10,
|
||||||
|
@ -621,7 +621,7 @@ CREATE [OR REPLACE] [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}] VIEW view_name
|
|||||||
|
|
||||||
### 锁表
|
### 锁表
|
||||||
|
|
||||||
```mysql
|
```sql
|
||||||
/* 锁表 */
|
/* 锁表 */
|
||||||
表锁定只用于防止其它客户端进行不正当地读取和写入
|
表锁定只用于防止其它客户端进行不正当地读取和写入
|
||||||
MyISAM 支持表锁,InnoDB 支持行锁
|
MyISAM 支持表锁,InnoDB 支持行锁
|
||||||
@ -633,7 +633,7 @@ MyISAM 支持表锁,InnoDB 支持行锁
|
|||||||
|
|
||||||
### 触发器
|
### 触发器
|
||||||
|
|
||||||
```mysql
|
```sql
|
||||||
/* 触发器 */ ------------------
|
/* 触发器 */ ------------------
|
||||||
触发程序是与表有关的命名数据库对象,当该表出现特定事件时,将激活该对象
|
触发程序是与表有关的命名数据库对象,当该表出现特定事件时,将激活该对象
|
||||||
监听:记录的增加、修改、删除。
|
监听:记录的增加、修改、删除。
|
||||||
@ -686,7 +686,7 @@ end
|
|||||||
|
|
||||||
### SQL 编程
|
### SQL 编程
|
||||||
|
|
||||||
```mysql
|
```sql
|
||||||
/* SQL编程 */ ------------------
|
/* SQL编程 */ ------------------
|
||||||
--// 局部变量 ----------
|
--// 局部变量 ----------
|
||||||
-- 变量声明
|
-- 变量声明
|
||||||
@ -821,7 +821,7 @@ INOUT,表示混合型
|
|||||||
|
|
||||||
### 存储过程
|
### 存储过程
|
||||||
|
|
||||||
```mysql
|
```sql
|
||||||
/* 存储过程 */ ------------------
|
/* 存储过程 */ ------------------
|
||||||
存储过程是一段可执行性代码的集合。相比函数,更偏向于业务逻辑。
|
存储过程是一段可执行性代码的集合。相比函数,更偏向于业务逻辑。
|
||||||
调用:CALL 过程名
|
调用:CALL 过程名
|
||||||
@ -842,7 +842,7 @@ END
|
|||||||
|
|
||||||
### 用户和权限管理
|
### 用户和权限管理
|
||||||
|
|
||||||
```mysql
|
```sql
|
||||||
/* 用户和权限管理 */ ------------------
|
/* 用户和权限管理 */ ------------------
|
||||||
-- root密码重置
|
-- root密码重置
|
||||||
1. 停止MySQL服务
|
1. 停止MySQL服务
|
||||||
@ -924,7 +924,7 @@ GRANT OPTION -- 允许授予权限
|
|||||||
|
|
||||||
### 表维护
|
### 表维护
|
||||||
|
|
||||||
```mysql
|
```sql
|
||||||
/* 表维护 */
|
/* 表维护 */
|
||||||
-- 分析和存储表的关键字分布
|
-- 分析和存储表的关键字分布
|
||||||
ANALYZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE 表名 ...
|
ANALYZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE 表名 ...
|
||||||
@ -937,7 +937,7 @@ OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...
|
|||||||
|
|
||||||
### 杂项
|
### 杂项
|
||||||
|
|
||||||
```mysql
|
```sql
|
||||||
/* 杂项 */ ------------------
|
/* 杂项 */ ------------------
|
||||||
1. 可用反引号(`)为标识符(库名、表名、字段名、索引、别名)包裹,以避免与关键字重名!中文也可以作为标识符!
|
1. 可用反引号(`)为标识符(库名、表名、字段名、索引、别名)包裹,以避免与关键字重名!中文也可以作为标识符!
|
||||||
2. 每个库目录存在一个保存当前数据库的选项文件db.opt。
|
2. 每个库目录存在一个保存当前数据库的选项文件db.opt。
|
||||||
|
@ -438,7 +438,7 @@ maxmemory-policy allkeys-lfu
|
|||||||
|
|
||||||
由于该命令对 Redis 性能的影响比较大,因此禁止长时间开启 `MONITOR`(生产环境中建议谨慎使用该命令)。
|
由于该命令对 Redis 性能的影响比较大,因此禁止长时间开启 `MONITOR`(生产环境中建议谨慎使用该命令)。
|
||||||
|
|
||||||
```java
|
```bash
|
||||||
# redis-cli
|
# redis-cli
|
||||||
127.0.0.1:6379> MONITOR
|
127.0.0.1:6379> MONITOR
|
||||||
OK
|
OK
|
||||||
|
@ -141,6 +141,74 @@ static class Entry extends WeakReference<ThreadLocal<?>> {
|
|||||||
1. 在使用完 `ThreadLocal` 后,务必调用 `remove()` 方法。 这是最安全和最推荐的做法。 `remove()` 方法会从 `ThreadLocalMap` 中显式地移除对应的 entry,彻底解决内存泄漏的风险。 即使将 `ThreadLocal` 定义为 `static final`,也强烈建议在每次使用后调用 `remove()`。
|
1. 在使用完 `ThreadLocal` 后,务必调用 `remove()` 方法。 这是最安全和最推荐的做法。 `remove()` 方法会从 `ThreadLocalMap` 中显式地移除对应的 entry,彻底解决内存泄漏的风险。 即使将 `ThreadLocal` 定义为 `static final`,也强烈建议在每次使用后调用 `remove()`。
|
||||||
2. 在线程池等线程复用的场景下,使用 `try-finally` 块可以确保即使发生异常,`remove()` 方法也一定会被执行。
|
2. 在线程池等线程复用的场景下,使用 `try-finally` 块可以确保即使发生异常,`remove()` 方法也一定会被执行。
|
||||||
|
|
||||||
|
### 如何跨线程传递 ThreadLocal 的值?
|
||||||
|
|
||||||
|
由于 `ThreadLocal` 的变量值存放在 `Thread` 里,而父子线程属于不同的 `Thread` 的。因此在异步场景下,父子线程的 `ThreadLocal` 值无法进行传递。
|
||||||
|
|
||||||
|
如果想要在异步场景下传递 `ThreadLocal` 值,有两种解决方案:
|
||||||
|
|
||||||
|
- `InheritableThreadLocal` :`InheritableThreadLocal` 是 JDK1.2 提供的工具,继承自 `ThreadLocal` 。使用 `InheritableThreadLocal` 时,会在创建子线程时,令子线程继承父线程中的 `ThreadLocal` 值,但是无法支持线程池场景下的 `ThreadLocal` 值传递。
|
||||||
|
- `TransmittableThreadLocal` : `TransmittableThreadLocal` (简称 TTL) 是阿里巴巴开源的工具。`TTL` 可以在线程池的场景下支持 `ThreadLocal` 值传递。
|
||||||
|
|
||||||
|
#### `InheritableThreadLocal` 原理扩展
|
||||||
|
|
||||||
|
`InheritableThreadLocal` 实现了创建异步线程时,继承父线程 `ThreadLocal` 值的功能。该类是 JDK 团队提供的,通过改造 JDK 源码包中的 `Thread` 类来实现创建线程时,`ThreadLocal` 值的传递。
|
||||||
|
|
||||||
|
**`InheritableThreadLocal` 的值存储在哪里?**
|
||||||
|
|
||||||
|
在 `Thread` 类中添加了一个新的 `ThreadLocalMap` ,命名为 `inheritableThreadLocals` ,该变量用于存储需要跨线程传递的 `ThreadLocal` 值。如下:
|
||||||
|
|
||||||
|
```JAVA
|
||||||
|
class Thread implements Runnable {
|
||||||
|
ThreadLocal.ThreadLocalMap threadLocals = null;
|
||||||
|
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**如何完成 `ThreadLocal` 值的传递?**
|
||||||
|
|
||||||
|
通过改造 `Thread` 类的构造方法来实现,在创建 `Thread` 线程时,拿到父线程的 `inheritableThreadLocals` 变量赋值给子线程即可。相关代码如下:
|
||||||
|
|
||||||
|
```JAVA
|
||||||
|
// Thread 的构造方法会调用 init() 方法
|
||||||
|
private void init(/* ... */) {
|
||||||
|
// 1、获取父线程
|
||||||
|
Thread parent = currentThread();
|
||||||
|
// 2、将父线程的 inheritableThreadLocals 赋值给子线程
|
||||||
|
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
|
||||||
|
this.inheritableThreadLocals =
|
||||||
|
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `TransmittableThreadLocal` 原理扩展
|
||||||
|
|
||||||
|
JDK 默认没有支持线程池场景下 `ThreadLocal` 值传递的功能,因此阿里巴巴开源了一套工具 `TransmittableThreadLocal` 来实现该功能。
|
||||||
|
|
||||||
|
阿里巴巴无法改动 JDK 的源码,因此他内部通过 **装饰器模式** 在原有的功能上做增强,以此来实现线程池场景下的 `ThreadLocal` 值传递。
|
||||||
|
|
||||||
|
TTL 改造的地方有两处:
|
||||||
|
|
||||||
|
- 实现自定义的 `Thread` ,在 `run()` 方法内部做 `ThreadLocal` 变量的赋值操作。
|
||||||
|
|
||||||
|
- 基于 **线程池** 进行装饰,在 `execute()` 方法中,不提交 JDK 内部的 `Thread` ,而是提交自定义的 `Thread` 。
|
||||||
|
|
||||||
|
如果想要查看相关源码,可以引入 Maven 依赖进行下载。
|
||||||
|
|
||||||
|
```XML
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>transmittable-thread-local</artifactId>
|
||||||
|
<version>2.12.0</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 相关应用场景
|
||||||
|
|
||||||
|
在 **线上服务压测** 场景下,会使用 `ThreadLocal` 存储压测标记,来区分压测流量和线上真实流量。
|
||||||
|
|
||||||
|
如果使用默认的 `ThreadLocal` ,就会导致在异步线程、线程池场景下, `ThreadLocal` 存储的压测标记丢失,从而造成比较严重的后果。
|
||||||
|
|
||||||
## 线程池
|
## 线程池
|
||||||
|
|
||||||
### 什么是线程池?
|
### 什么是线程池?
|
||||||
@ -405,7 +473,7 @@ public class ThreadPoolTest {
|
|||||||
|
|
||||||
输出:
|
输出:
|
||||||
|
|
||||||
```ba
|
```bash
|
||||||
18:19:48.203 INFO [pool-1-thread-1] c.j.concurrent.ThreadPoolTest - 核心线程执行第一个任务
|
18:19:48.203 INFO [pool-1-thread-1] c.j.concurrent.ThreadPoolTest - 核心线程执行第一个任务
|
||||||
18:19:48.203 INFO [pool-1-thread-2] c.j.concurrent.ThreadPoolTest - 非核心线程处理第三个任务
|
18:19:48.203 INFO [pool-1-thread-2] c.j.concurrent.ThreadPoolTest - 非核心线程处理第三个任务
|
||||||
18:19:48.203 INFO [main] c.j.concurrent.ThreadPoolTest - 主线程处理第四个任务
|
18:19:48.203 INFO [main] c.j.concurrent.ThreadPoolTest - 主线程处理第四个任务
|
||||||
|
@ -18,7 +18,7 @@ footer: |-
|
|||||||
|
|
||||||
## 关于网站
|
## 关于网站
|
||||||
|
|
||||||
JavaGuide 已经持续维护 6 年多了,累计提交了 **5500+** commit ,共有 **520+** 多位贡献者共同参与维护和完善。真心希望能够把这个项目做好,真正能够帮助到有需要的朋友!
|
JavaGuide 已经持续维护 6 年多了,累计提交了 **5600+** commit ,共有 **550+** 多位贡献者共同参与维护和完善。真心希望能够把这个项目做好,真正能够帮助到有需要的朋友!
|
||||||
|
|
||||||
如果觉得 JavaGuide 的内容对你有帮助的话,还请点个免费的 Star(绝不强制点 Star,觉得内容不错有收货再点赞就好),这是对我最大的鼓励,感谢各位一路同行,共勉!传送门:[GitHub](https://github.com/Snailclimb/JavaGuide) | [Gitee](https://gitee.com/SnailClimb/JavaGuide)。
|
如果觉得 JavaGuide 的内容对你有帮助的话,还请点个免费的 Star(绝不强制点 Star,觉得内容不错有收货再点赞就好),这是对我最大的鼓励,感谢各位一路同行,共勉!传送门:[GitHub](https://github.com/Snailclimb/JavaGuide) | [Gitee](https://gitee.com/SnailClimb/JavaGuide)。
|
||||||
|
|
||||||
|
25
package.json
25
package.json
@ -19,20 +19,19 @@
|
|||||||
"**/*": "prettier --write --ignore-unknown",
|
"**/*": "prettier --write --ignore-unknown",
|
||||||
".md": "markdownlint-cli2"
|
".md": "markdownlint-cli2"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.11.0",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vuepress/bundler-vite": "2.0.0-rc.15",
|
"@vuepress/bundler-vite": "2.0.0-rc.19",
|
||||||
"@vuepress/plugin-feed": "2.0.0-rc.3",
|
"@vuepress/plugin-feed": "2.0.0-rc.66",
|
||||||
"@vuepress/plugin-search": "2.0.0-rc.47",
|
"@vuepress/plugin-search": "2.0.0-rc.66",
|
||||||
"husky": "9.1.6",
|
"husky": "9.1.7",
|
||||||
"markdownlint-cli2": "0.14.0",
|
"markdownlint-cli2": "0.16.0",
|
||||||
"mathjax-full": "3.2.2",
|
"mathjax-full": "3.2.2",
|
||||||
"nano-staged": "0.8.0",
|
"nano-staged": "0.8.0",
|
||||||
"nodejs-jieba": "0.1.2",
|
"prettier": "3.4.2",
|
||||||
"prettier": "3.3.3",
|
"sass-embedded": "1.83.0",
|
||||||
"sass-embedded": "1.79.3",
|
"vue": "^3.5.13",
|
||||||
"vue": "^3.5.8",
|
"vuepress": "2.0.0-rc.19",
|
||||||
"vuepress": "2.0.0-rc.15",
|
"vuepress-theme-hope": "2.0.0-rc.64"
|
||||||
"vuepress-theme-hope": "2.0.0-rc.56"
|
},
|
||||||
}
|
"packageManager": "pnpm@9.15.0"
|
||||||
}
|
}
|
||||||
|
2793
pnpm-lock.yaml
generated
2793
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user