Java 并发编程】线程简介 ( 原子操作 | volatile 关键字使用场景 )

网友投稿 539 2022-05-29

文章目录

一、原子操作

二、volatile 关键字使用场景

一、原子操作

原子操作 :

read : 从

主内存

中的线程共享变量中读取数据 ;

load : 将从主内存读取到的数据 , 加载到

线程工作内存

中 ;

read 和 load 操作一定是

成对出现

【Java 并发编程】线程简介 ( 原子操作 | volatile 关键字使用场景 )

的 , 只要从主内存中读取到数据 , 一定会将这个数据加载到线程的工作内存中 ;

use : 从线程共享变量副本读取到线程的

执行引擎

中 ;

assign : 从执行引擎中写出数据到变量的

共享变量副本

中 ;

store : 将数据从线程工作内存传输到

主内存

中 ;

write : 将数据赋值给主内容中的线程

共享变量 ;

lock : 作用于

主内存中的线程共享变量

, 将该变量标识为

被某个线程独自占用状态

; 表示该变量只有一个线程可以进行访问 ;

unlock :

解锁

主内存中的共享变量 , 其它线程可以进行访问 ;

二、volatile 关键字使用场景

在下面的示例中 , 设置一个标志位 , 主线程开始后 , 启动一个线程 , 休眠 1000 1000 1000 毫秒 , 然后修改该标志位 , 主线程中根据标志位进行循环 , 如果标志位被修改 , 则循环停止 , 但是循环一直没有停止 ;

也就是说线程中修改的值 , 仅修改了该线程中工作内存中的标志位副本的值 ;

主内存中的值没有被修改 ;

代码示例 :

public class Main { private static boolean flag = false; private static void changeFlag() { System.out.println("修改标志位开始"); flag = true; System.out.println("修改标志位结束"); } public static void main(String[] args) { // 在该线程中 , 1 秒后修改标志位为 false new Thread(){ @Override public void run() { super.run(); try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } changeFlag(); } }.start(); // 此处如果 flag 一直为 flase 就会进入死循环 // 如果 flag 为 true 则程序结束 while (!flag) { } System.out.println("主线程结束"); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

执行结果 :

原理分析 :

线程的工作内存中 , 将 flag 修改为 true , 这只是在

CPU 缓存

中修改的 ,

没有在主内存中修改这个共享变量值

, 因此主线程访问该值 , 还是 false ;

使用 volatile 关键字 , 禁用 CPU 的缓存 , 直接在主内存中进行读写 , 这样就可以解决多个线程中 共享变量 不同步的问题 ;

注意 : 只能是 线程共享变量 使用该关键字 , 设置该关键字会影响线程的执行效率 , 效率会降低 ;

使用了 volatile 关键字后的效果 :

public class Main { private static volatile boolean flag = false; private static void changeFlag() { System.out.println("修改标志位开始"); flag = true; System.out.println("修改标志位结束"); } public static void main(String[] args) { // 在该线程中 , 1 秒后修改标志位为 false new Thread(){ @Override public void run() { super.run(); try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } changeFlag(); } }.start(); // 此处如果 flag 一直为 flase 就会进入死循环 // 如果 flag 为 true 则程序结束 while (!flag) { } System.out.println("主线程结束"); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

执行结果 :

Java 并发的 3 3 3 特性 :

原子性 : 每个操作都是

不可拆分的原子操作

; 在线程中进行 a++ 就不是原子操作 , 该操作分为 3 3 3 个步骤 , 首先从主内存中读取 a 变量 , 然后进行自增操作 , 最后在将自增后的值写回主内存中 ;

可见性 :

多个线程

访问同一个变量 , 该变量一旦被

某个线程修改

, 这些线程必须可以

立刻看到被修改的值 ;

有序性 : 程序按照

代码先后顺序

执行 ;

volatile 关键字 , 禁用了 CPU 缓存 , 解决的是共享变量可见性问题 ;

Java 任务调度

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:【嵌入式Linux基础入门】3、设置虚拟机和实体机的共享文件夹
下一篇:敏捷框架大PK:Scrum 方法 vs 看板方法 vs 精益开发 vs 极限编程
相关文章