Skip to content

Commit 9c6ecc1

Browse files
committed
blog
1 parent 9f3099a commit 9c6ecc1

94 files changed

Lines changed: 1649 additions & 1448 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/.vuepress/config.js

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ function genJavaSidebar() {
9595
collapsable: false,
9696
children: [
9797
"JUC/Java-Memory-Model",
98+
"JUC/volatile","JUC/synchronized","JUC/CAS",
9899
"JUC/CountDownLatch、CyclicBarrier、Semaphore"
99100
]
100101
}
@@ -146,14 +147,14 @@ function genInterviewSidebar(){
146147
['Collections-FAQ', 'Java集合面试'],
147148
['JUC-FAQ', 'Java 多线程面试'],
148149
['JVM-FAQ', 'JVM 面试'],
149-
// ['MySQL-FAQ', 'MySQL 面试'],
150-
// ['Redis-FAQ', 'Redis 面试'],
151-
// ['Network-FAQ', '计算机网络面试'],
152-
// ['Kafka-FAQ', 'Kafka 面试'],
153-
// ['Zookeeper-FAQ', 'Zookeeper 面试'],
154-
// ['MyBatis-FAQ', 'MyBatis 面试'],
155-
// ['Spring-FAQ', 'Spring 面试'],
156-
// ['Design-Pattern-FAQ', '设计模式面试'],
157-
// ['Tomcat-FAQ', 'Tomcat 面试'],
150+
['MySQL-FAQ', 'MySQL 面试'],
151+
['Redis-FAQ', 'Redis 面试'],
152+
['Network-FAQ', '计算机网络面试'],
153+
['Kafka-FAQ', 'Kafka 面试'],
154+
['ZooKeeper-FAQ', 'Zookeeper 面试'],
155+
['MyBatis-FAQ', 'MyBatis 面试'],
156+
['Spring-FAQ', 'Spring 面试'],
157+
['Design-Pattern-FAQ', '设计模式面试'],
158+
['Tomcat-FAQ', 'Tomcat 面试'],
158159
];
159160
}

docs/interview/MySQL-FAQ.md

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
![](https://imgkr.cn-bj.ufileos.com/9e22c4b4-0db5-4f4f-9636-974875d4018f.jpg)
1+
# MySQL 三万字精华总结 + 面试100 问,和面试官扯皮绰绰有余
22

33
> 写在之前:不建议那种上来就是各种面试题罗列,然后背书式的去记忆,对技术的提升帮助很小,对正经面试也没什么帮助,有点东西的面试官深挖下就懵逼了。
44
>
55
> 个人建议把面试题看作是费曼学习法中的回顾、简化的环节,准备面试的时候,跟着题目先自己讲给自己听,看看自己会满意吗,不满意就继续学习这个点,如此反复,好的offer离你不远的,奥利给
66
7-
> 文章收录在 GitHub [JavaKeeper](https://github.com/Jstarfish/JavaKeeper) ,N线互联网开发必备技能兵器谱,笔记自取。
8-
97
## 一、MySQL架构
108

119
和其它数据库相比,MySQL有点与众不同,它的架构可以在多种不同场景中应用并发挥良好作用。主要体现在存储引擎的架构上,**插件式的存储引擎架构将查询处理和其它的系统任务以及数据的存储提取相分离**。这种架构可以根据业务的需求和实际需要选择合适的存储引擎。
1210

13-
![](https://img2018.cnblogs.com/blog/1383365/201902/1383365-20190201092513900-638761565.png)
11+
![](https://tva1.sinaimg.cn/large/007S8ZIlly1gj0sffrl6ij30t30gqdlf.jpg)
1412

1513

1614

@@ -884,7 +882,7 @@ MySQL 从 5.0.3 InnoDB 存储引擎开始支持XA协议的分布式事务。一
884882

885883
在MySQL中,使用分布式事务涉及一个或多个资源管理器和一个事务管理器。
886884

887-
![](https://imgkr.cn-bj.ufileos.com/8d48c5e1-c849-413e-8e5a-e96529235f58.png)
885+
![](https://tva1.sinaimg.cn/large/007S8ZIlly1gj0sqpwsvdj30k009dtak.jpg)
888886

889887
如图,MySQL 的分布式事务模型。模型中分三块:应用程序(AP)、资源管理器(RM)、事务管理器(TM):
890888

@@ -1284,7 +1282,7 @@ Shell> mysqladmin extended-status -u username -p password——显示状态信
12841282

12851283
![explain-demo](https://tva1.sinaimg.cn/large/007S8ZIlly1gf2hszmc0lj30lc05w75c.jpg)
12861284

1287-
1. 第一行(执行顺序4):id列为1,表示是union里的第一个select,select_type列的primary表示该查询为外层查询,table列被标记为<derived3>,表示查询结果来自一个衍生表,其中derived3中3代表该查询衍生自第三个select查询,即id为3的select。【select d1.name......】
1285+
1. 第一行(执行顺序4):id列为1,表示是union里的第一个select,select_type列的primary表示该查询为外层查询,table列被标记为\<derived3>,表示查询结果来自一个衍生表,其中derived3中3代表该查询衍生自第三个select查询,即id为3的select。【select d1.name......】
12881286

12891287
2. 第二行(执行顺序2):id为3,是整个查询中第三个select的一部分。因查询包含在from中,所以为derived。【select id,name from t1 where other_column=''】
12901288
3. 第三行(执行顺序3):select列表中的子查询select_type为subquery,为整个查询中的第二个select。【select id from t3】
@@ -1692,8 +1690,6 @@ https://juejin.im/post/5e3eb616f265da570d734dcb#heading-105
16921690

16931691
https://blog.csdn.net/yin767833376/article/details/81511377
16941692

1695-
![](https://imgkr.cn-bj.ufileos.com/6e7c80a9-48e6-4a2a-b920-682d8f0bab5c.png)
1696-
16971693

16981694

16991695

docs/java/Assert.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Assert
2+
3+
## 概述
4+
5+
业务代码中我们是不会使用断言的,但是看各种源码或单元测试的时候,肯定会遇到 Assert,你有了解过吗?这玩意到底是干嘛的?
6+
7+
> 编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设,可以将断言看作是**异常处理**的一种高级形式。断言表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真。可以在任何时候启用和禁用断言验证,因此可以在测试时启用断言,而在部署时禁用断言。同样,程序投入运行后,最终用户在遇到问题时可以重新启用断言。
8+
9+
Assert 其实就是用来调试程序的
10+
11+
java 断言 assert 是 jdk1.4 引入的。
12+
13+
  jvm断言默认是关闭的。
14+
15+
断言可以局部开启的,如:父类禁止断言,而子类开启断言,所以一般说“**断言不具有继承性**”。
16+
17+
**断言只适用复杂的调式过程。**
18+
19+
**断言一般用于程序执行结构的判断,千万不要让断言处理业务流程。**
20+
21+
22+
23+
## 语法
24+

docs/java/JUC/JUC.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
### Java JUC 简介
1+
### 说说 JUC
22

3-
在 Java 5.0 提供了 `java.util.concurrent` (简称 JUC )包,在此包中增加了在并发编程中很常用 的实用工具类,用于定义类似于线程的自定义子 系统,包括线程池、异步 IO 和轻量级任务框架。 提供可调的、灵活的线程池。还提供了设计用于 多线程上下文中的 Collection 实现等。
3+
在 Java 5.0 提供了 `java.util.concurrent` (简称 JUC )包,在此包中增加了在并发编程中很常用的实用工具类,用于定义类似于线程的自定义子 系统,包括线程池、异步 IO 和轻量级任务框架。 提供可调的、灵活的线程池。还提供了设计用于 多线程上下文中的 Collection 实现等。
44

55

66

docs/java/JUC/Java-Memory-Model.md

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,17 @@
2727

2828
我们以多核 CPU 为例,每个CPU 核都包含**一组 「CPU 寄存器」**,这些寄存器本质上是在 CPU 内存中。CPU 在这些寄存器上执行操作的速度要比在主内存(RAM)中执行的速度快得多。
2929

30-
因为**CPU速率高, 内存速率慢,为了让存储体系可以跟上CPU的速度,所以中间又加上 Cache 层,就是我们说的 CPU 高速缓存**
30+
因为**CPU速率高, 内存速率慢,为了让存储体系可以跟上 CPU 的速度,所以中间又加上 Cache 层,就是我们说的 CPU 高速缓存**
3131

3232
### CPU多级缓存
3333

34-
由于CPU的运算速度远远超越了1级缓存的数据I\O能力,CPU厂商又引入了多级的缓存结构。通常L1、L2 是每个CPU 核有一个,L3 是多个核共用一个。
34+
由于 CPU 的运算速度远远超越了 1 级缓存的数据 I\O 能力,CPU 厂商又引入了多级的缓存结构。通常 L1、L2 是每个 CPU 核有一个,L3 是多个核共用一个。
3535

3636
### Cache Line
3737

38-
Cache又是由很多个**「缓存行」**(Cache line) 组成的。Cache line 是 Cache 和 RAM 交换数据的最小单位。
38+
Cache 又是由很多个**「缓存行」**(Cache line) 组成的。Cache line 是 Cache 和 RAM 交换数据的最小单位。
3939

40-
Cache 存储数据是固定大小为单位的,称为一个**Cache entry**,这个单位称为**Cache line****Cache block**给定Cache 容量大小和 Cache line size 的情况下,它能存储的条目个数(number of cache entries)就是固定的。因为Cache 是固定大小的,所以它从主内存获取数据也是固定大小。对于X86来讲,是 64Bytes。对于ARM来讲,较旧的架构的Cache line是32Bytes,但一次内存访存只访问一半的数据也不太合适,所以它经常是一次填两个 Cache line,叫做 double fill。
40+
Cache 存储数据是固定大小为单位的,称为一个**Cache entry**,这个单位称为 **Cache line****Cache block**给定 Cache 容量大小和 Cache line size 的情况下,它能存储的条目个数(number of cache entries)就是固定的。因为Cache 是固定大小的,所以它从主内存获取数据也是固定大小。对于 X86 来讲,是 64Bytes。对于 ARM 来讲,较旧的架构的 Cache line 是 32Bytes,但一次内存访存只访问一半的数据也不太合适,所以它经常是一次填两个 Cache line,叫做 double fill。
4141

4242

4343

@@ -65,13 +65,13 @@ Cache 存储数据是固定大小为单位的,称为一个**Cache entry**,
6565

6666
总线锁就是使用 CPU 提供的一个`LOCK#`信号,当一个处理器在总线上输出此信号,其他处理器的请求将被阻塞,那么该处理器就可以独占共享锁。这样就保证了数据一致性。
6767

68-
但是总线锁开销太大,我们需要控制锁的粒度,所以又有了缓存锁,核心就是“**缓存一致性协议**”,不同的 CPU 硬件厂商实现方式稍有不同,有MSI、MESI、MOSI等。
68+
但是总线锁开销太大,我们需要控制锁的粒度,所以又有了缓存锁,核心就是“**缓存一致性协议**”,不同的 CPU 硬件厂商实现方式稍有不同,有 MSI、MESI、MOSI等。
6969

7070

7171

7272
### 代码乱序执行优化
7373

74-
为了使得处理器内部的运算单元尽量被充分利用,提高运算效率,处理器可能会对输入的代码进行「乱序执行」**(Out-Of-Order Execution),处理器会在计算之后将乱序执行的结果重组**,乱序优化可以保证在单线程下该执行结果与顺序执行的结果是一致的,但不保证程序中各个语句计算的先后顺序与输入代码中的顺序一致。
74+
为了使得处理器内部的运算单元尽量被充分利用,提高运算效率,处理器可能会对输入的代码进行「**乱序执行****(Out-Of-Order Execution),处理器会在计算之后将乱序执行的结果重组**,乱序优化可以保证在单线程下该执行结果与顺序执行的结果是一致的,但不保证程序中各个语句计算的先后顺序与输入代码中的顺序一致。
7575

7676
**乱序执行技术是处理器为提高运算速度而做出违背代码原有顺序的优化**。在单核时代,处理器保证做出的优化不会导致执行结果远离预期目标,但在多核环境下却并非如此。
7777

@@ -84,9 +84,7 @@ Cache 存储数据是固定大小为单位的,称为一个**Cache entry**,
8484

8585
### 内存屏障
8686

87-
又称为内存栅栏,是一个 CPU 指令。尽管我们看到乱序执行初始目的是为了提高效率,但是它看来其好像在这多核时代不尽人意,其中的某些”自作聪明”的优化导致多线程程序产生各种各样的意外。因此有必要存在一种机制来消除乱序执行带来的坏影响,也就是说应该允许程序员显式的告诉处理器对某些地方禁止乱序执行。这种机制就是所谓内存屏障。不同架构的处理器在其指令集中提供了不同的指令来发起内存屏障,对应在编程语言当中就是提供特殊的关键字来调用处理器相关的指令,JMM里我们再探讨。
88-
89-
87+
又称为内存栅栏,是一个 CPU 指令。尽管我们看到乱序执行初始目的是为了提高效率,但是它看来其好像在这多核时代不尽人意,其中的某些”自作聪明”的优化导致多线程程序产生各种各样的意外。因此有必要存在一种机制来消除乱序执行带来的坏影响,也就是说应该允许程序员显式的告诉处理器对某些地方禁止乱序执行。这种机制就是所谓内存屏障。不同架构的处理器在其指令集中提供了不同的指令来发起内存屏障,对应在编程语言当中就是提供特殊的关键字来调用处理器相关的指令,JMM 里我们再探讨。
9088

9189
------
9290

@@ -148,9 +146,9 @@ JMM 与 Java 内存区域中的堆、栈、方法区等并不是同一个层次
148146

149147
如果两个或多个线程共享一个对象,并且多个线程更新该共享对象中的变量,则可能出现竞争条件。
150148

151-
想象一下,如果线程A将一个共享对象的变量读入到它的CPU缓存中。此时,线程B执行相同的操作,但是进入不同的CPU缓存。现在线程A执行 +1 操作,线程B也这样做。现在该变量增加了两次,在每个CPU缓存中一次
149+
想象一下,如果线程 A 将一个共享对象的变量读入到它的 CPU 缓存中。此时,线程 B 执行相同的操作,但是进入不同的 CPU 缓存。现在线程A执行 +1 操作,线程B也这样做。现在该变量增加了两次,在每个 CPU 缓存中一次
152150

153-
如果这些增量是按顺序执行的,则变量结果会是3,并将原始值+ 2写回主内存。但是,这两个增量是同时执行的,没有适当的同步。不管将哪个线程的结构写回主内存,更新后的值只比原始值高1,显然是有问题的。如下(当然可以用 Java 提供的关键字 Synchronized)
151+
如果这些增量是按顺序执行的,则变量结果会是 3,并将原始值 +2 写回主内存。但是,这两个增量是同时执行的,没有适当的同步。不管将哪个线程的结构写回主内存,更新后的值只比原始值高1,显然是有问题的。如下(当然可以用 Java 提供的关键字 Synchronized)
154152

155153
![](https://tva1.sinaimg.cn/large/00831rSTly1gcw2i23173j30pu0hqgml.jpg)
156154

@@ -218,15 +216,15 @@ Java 内存模型要求 lock,unlock,read,load,assign,use,store,wri
218216

219217
先行发生(happens-before)是 Java 内存模型中定义的两项操作之间的偏序关系,**如果操作A 先行发生于操作B,那么A的结果对B可见**。happens-before关系的分析需要分为**单线程和多线程**的情况:
220218

221-
- **单线程下的 happens-before** 字节码的先后顺序天然包含happens-before关系:因为单线程内共享一份工作内存,不存在数据一致性的问题。 在程序控制流路径中靠前的字节码 happens-before 靠后的字节码,即靠前的字节码执行完之后操作结果对靠后的字节码可见。然而,这并不意味着前者一定在后者之前执行。实际上,如果后者不依赖前者的运行结果,那么它们可能会被重排序。
222-
- **多线程下的 happens-before** 多线程由于每个线程有共享变量的副本,如果没有对共享变量做同步处理,线程1更新执行操作A共享变量的值之后,线程2开始执行操作B,此时操作A产生的结果对操作B不一定可见
219+
- **单线程下的 happens-before** 字节码的先后顺序天然包含 happens-before 关系:因为单线程内共享一份工作内存,不存在数据一致性的问题。 在程序控制流路径中靠前的字节码 happens-before 靠后的字节码,即靠前的字节码执行完之后操作结果对靠后的字节码可见。然而,这并不意味着前者一定在后者之前执行。实际上,如果后者不依赖前者的运行结果,那么它们可能会被重排序。
220+
- **多线程下的 happens-before** 多线程由于每个线程有共享变量的副本,如果没有对共享变量做同步处理,线程 1 更新执行操作 A 共享变量的值之后,线程 2 开始执行操作 B,此时操作 A 产生的结果对操作 B 不一定可见
223221

224-
为了方便程序开发,Java 内存模型实现了下述的先行发生关系:
222+
为了方便程序开发,Java 内存模型实现了下述的先行发生关系(“天然的”先行发生关系,无需任何同步器协助就存在)
225223

226224
- **程序次序规则:** 一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作。
227-
- **管程锁定规则:** 一个unLock操作先行发生于后面对同一个锁的lock操作
228-
- **volatile变量规则:** 对一个变量的写操作 happens-before 后面对这个变量的读操作
229-
- **传递规则:** 如果操作A 先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A 先行发生于操作C
225+
- **管程锁定规则:** 一个 unLock 操作先行发生于后面对同一个锁的 lock 操作
226+
- **volatile变量规则:** 对一个变量的写操作先行发生于后面对这个变量的读操作
227+
- **传递规则:** 如果操作 A 先行发生于操作 B,而操作 B 又先行发生于操作 C,则可以得出操作 A 先行发生于操作 C
230228
- **线程启动规则:** Thread对象的 `start()` 方法先行发生于此线程的每一个动作。
231229
- **线程中断规则:** 对线程 `interrupt()` 方法的调用先行发生于被中断线程的代码检测到中断事件的发生。
232230
- **线程终结规则:** 线程中所有的操作都先行发生于线程的终止检测,我们可以通过`Thread.join()`方法结束、`Thread.isAlive()`的返回值手段检测到线程已经终止执行。
@@ -252,7 +250,7 @@ Load2;
252250
Load3;
253251
```
254252

255-
对于上面的一组 CPU 指令(Store表示写入指令,Load表示读取指令),StoreLoad 屏障之前的 Store 指令无法与StoreLoad 屏障之后的 Load 指令进行交换位置,即**重排序**。但是 StoreLoad 屏障之前和之后的指令是可以互换位置的,即 Store1 可以和 Store2 互换,Load2 可以和 Load3 互换。
253+
对于上面的一组 CPU 指令(Store表示写入指令,Load表示读取指令),StoreLoad 屏障之前的 Store 指令无法与 StoreLoad 屏障之后的 Load 指令进行交换位置,即**重排序**。但是 StoreLoad 屏障之前和之后的指令是可以互换位置的,即 Store1 可以和 Store2 互换,Load2 可以和 Load3 互换。
256254

257255
常见的 4 种屏障
258256

docs/java/JUC/synchronized.md

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,17 @@
1+
# Synchronized 关键字
2+
13
https://www.cnblogs.com/aspirant/p/11470858.html
24

35
## 一、前言
46

5-
```java
6-
7-
public class SynchronizedDemo implements Runnable{
8-
9-
private static int count = 0;
10-
11-
public static void main(String[] args) {
12-
for (int i = 0; i < 10; i++) {
13-
Thread thread = new Thread(new SynchronizedDemo());
14-
thread.start();
15-
}
16-
try {
17-
Thread.sleep(500);
18-
} catch (InterruptedException e) {
19-
e.printStackTrace();
20-
}
21-
System.out.println("result: " + count);
22-
}
23-
24-
@Override
25-
public void run() {
26-
for (int i = 0; i < 1000000; i++) {
27-
count++;
28-
}
29-
}
30-
31-
}
32-
```
337

34-
开启了 10 个线程,每个线程都累加了 1000000次,如果结果正确的话自然而然总数就应该是10 * 1000000 = 10000000。可就运行多次结果都不是这个数,而且每次运行结果都不一样。这是为什么了?有什么解决方案了?这就是我们今天要聊的事情。
358

369

3710

3811
## 二、使用
3912

13+
每个初学多线程的 Javaer,在遇到这种多线程问题的时候,肯定会先想到 Synchronized,我们把它称为“同步”,
14+
4015
Synchronized 是 Java 中解决并发问题的一种最常用的方法,也是最简单的一种方法。Synchronized 的作用主要有三个:
4116

4217
1. 原子性:确保线程互斥的访问同步代码;

0 commit comments

Comments
 (0)