Skip to content

Commit 9da09d1

Browse files
committed
更新 docs/10.并发多线程/01.volatile.md
添加 volatile 禁止指令重排demo
1 parent 7b0ca1d commit 9da09d1

File tree

2 files changed

+103
-17
lines changed

2 files changed

+103
-17
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.jackson0714.passjava.threads;
2+
3+
/**
4+
演示volatile 禁止重排
5+
* @author: 悟空聊架构
6+
* @create: 2020-08-13
7+
*/
8+
9+
10+
public class VolatileResort {
11+
12+
static int num = 0;
13+
static boolean flag = false;
14+
15+
public static void init() {
16+
num= 1;
17+
flag = true;
18+
}
19+
20+
public static void add() {
21+
22+
while (flag) {
23+
flag = false;
24+
num = num + 100;
25+
System.out.println("num:" + num);
26+
}
27+
}
28+
29+
public static void main(String[] args) throws InterruptedException {
30+
31+
new Thread(() -> {
32+
add();
33+
},"子线程").start();
34+
init();
35+
36+
}
37+
}

docs/10.并发多线程/01.volatile.md

Lines changed: 66 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# 万字长文 | 我想跟你聊一宿的Java volatile | 附示例
1+
# 万字长文 | 再也不怕面试官问 volatile!!!
22

33
# 一、Volatile怎么念?
44

@@ -304,7 +304,7 @@ number++其实执行了`3条指令`:
304304

305305
## 7.1 synchronized同步代码块
306306

307-
我们可以通过使用synchorized同步代码块来保证原子性。从而使结果等于20000
307+
我们可以通过使用synchronized同步代码块来保证原子性。从而使结果等于20000
308308

309309
``` java
310310
public synchronized static void increase() {
@@ -360,11 +360,11 @@ public static void main(String[] args) {
360360

361361
会不会感觉到重排把指令顺序都打乱了,这样好吗?
362362

363-
可以回想下小学时候的数学题:2+3-3=?,如果把运算顺序改为3-3+2,结果也是一样的。所以指令重排是要保证程序结果不变的情况下做重排
363+
可以回想下小学时候的数学题:`2+3-5=?`,如果把运算顺序改为`3-5+2=?`,结果也是一样的。所以指令重排是要保证单线程下程序结果不变的情况下做重排
364364

365365
## 8.1 为什么要重排
366366

367-
计算机在执行程序时,在执行程序时,为了提高性能,编译器和处理器常常会对指令做重排序。
367+
计算机在执行程序时,为了提高性能,编译器和处理器常常会对指令做重排序。
368368

369369
## 8.2 有哪几种重排
370370

@@ -384,39 +384,88 @@ public static void main(String[] args) {
384384

385385
- 多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的,结果无法预测。
386386

387-
# 、volatile什么场景下会用到
387+
## 8.3 举个例子来说说多线程中的指令重排
388388

389-
奇怪的是,volatile都不保证原子性,为啥我们还要用它?
389+
设想一下这种场景:定义了变量num=0和变量flag=false,线程1调用初始化函数init()执行后,num=1,flag=true,当另外线程判断flag=true后,执行num+100操作,那么我们预期的结果是num会等于101,但因为有指令重排的可能,num=1和flag=true执行顺序可能会颠倒,以至于num可能等于100
390390

391-
volatile是轻量级的同步机制,对性能的影响比synchorized小。
391+
```java
392392

393-
> 典型的用法:检查某个状态标记以判断是否退出循环。
394393

395-
比如线程试图通过类似于数绵羊的传统方法进入休眠状态,为了使这个示例能正确执行,asleep必须为volatile变量。否则,当asleep被另一个线程修改时,执行判断的线程却发现不了。
394+
num= 1;
395+
flag = true;
396396

397-
**那为什么我们不直接用synchorized,lock锁?它们既可以保证可见性,又可以保证原子性为何不用呢?**
397+
的执行顺序变为
398398

399-
因为synchorized和lock是排他锁(悲观锁),如果有多个线程需要访问这个变量,将会发生竞争,只有一个线程可以访问这个变量,其他线程被阻塞了,会影响程序的性能。
399+
flag = true;
400400

401-
> 注意:当且仅当满足以下所有条件时,才应该用volatile变量
402-
>
403-
> - 对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值。
404-
> - 该变量不会与其他的状态一起纳入不变性条件中。
405-
> - 在访问变量不需要加锁。
401+
num= 1;public class VolatileResort {
406402

403+
static int num = 0;
404+
static boolean flag = false;
407405

406+
public static void init() {
407+
num= 1;
408+
flag = true;
409+
}
408410

411+
public static void add() {
412+
if (flag) {
413+
num = num + 5;
414+
System.out.println("num:" + num);
415+
}
416+
}
409417

418+
public static void main(String[] args) {
419+
init();
420+
new Thread(() -> {
421+
add();
422+
},"子线程").start();
423+
}
424+
}
425+
/*
426+
num= 1;
427+
flag = true;
428+
if (flag) {
429+
num = num + 5;
430+
System.out.println("num:" + num);
431+
}
432+
的执行顺序变为
433+
434+
flag = true;
435+
if (flag) {
436+
num = num + 5;
437+
System.out.println("num:" + num);
438+
}
439+
num= 1;
440+
*/
441+
```
410442

411443

444+
412445

413-
# 、主线程是如何立即感知到变量变了呢?
414446

415447

448+
# # 十、volatile什么场景下会用到?
416449

450+
奇怪的是,volatile都不保证原子性,为啥我们还要用它?
417451

452+
volatile是轻量级的同步机制,对性能的影响比synchronized小。
418453

454+
> 典型的用法:检查某个状态标记以判断是否退出循环。
455+
456+
比如线程试图通过类似于数绵羊的传统方法进入休眠状态,为了使这个示例能正确执行,asleep必须为volatile变量。否则,当asleep被另一个线程修改时,执行判断的线程却发现不了。
457+
458+
**那为什么我们不直接用synchorized,lock锁?它们既可以保证可见性,又可以保证原子性为何不用呢?**
459+
460+
因为synchorized和lock是排他锁(悲观锁),如果有多个线程需要访问这个变量,将会发生竞争,只有一个线程可以访问这个变量,其他线程被阻塞了,会影响程序的性能。
461+
462+
> 注意:当且仅当满足以下所有条件时,才应该用volatile变量
463+
>
464+
> - 对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值。
465+
> - 该变量不会与其他的状态一起纳入不变性条件中。
466+
> - 在访问变量不需要加锁。
419467
468+
# 十一、volatile和synchronized
420469

421470

422471

0 commit comments

Comments
 (0)