JAVA多线程基础篇 4、可见性、有序性与Volatile
文章目录
- 1. 可见性问题和有序性问题
- # 2. 可见性问题的实验
- 2.1 volatile确保了可见性
- 3. 一个指令乱序的实验
- 总结
1. 可见性问题和有序性问题
在多线程开发中,可见性问题和有序性问题需要依托volatile来保证。
可见性,一个线程对共享变量的修改,另外一个线程能够立刻看到。
首先需要了解一个概念,堆和栈。
程序在运行的过程中,数据放在堆中,线程内部的缓存数据会放在栈中。
在堆中的数据是公共的,而在线程的栈帧里的数据是缓存各自线程的。
因此,当A、B两个线程,无论谁更新了线程内的数据,都及时同步并告知其他线程重新刷新读取缓存。java使用volatile 修饰的变量,通过操作系统底层的锁总线(LOCK#)或者缓存一致性协议(MESI)实现可见性。
- Lock 会对总线进行锁定,其它 CPU 对内存的读写请求都会被阻塞,直到锁释放。LOCK的开销比较大,锁总线期间其他 CPU 没法访问内存。于是引入了缓存一致性协议(MESI)来实现。
- MESI 当一个缓存代表它所属的处理器去读写内存时,其它处理器都会得到通知,它们以此来使自己的缓存保持同步。 只要某个处理器写内存,其它处理器马上知道这块内存在它们的缓存段中已经失效。
有序性,即程序执行的顺序按照代码的先后顺序执行。
在执行程序时为了提高性能,编译器和处理器常常会对指令做重排序。
- 编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
- 指令级并行的重排序。现代处理器采用了指令级并行技术(Instruction-Level Parallelism, ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
- 内存系统的重排序。由于处理器使用缓存和读 / 写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。
通过synchronized和Lock来保证有序性,synchronized和Lock保证每个时刻是有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。
通过使用volatile来保证有序性,对一个 volatile 域的写,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。
# 2. 可见性问题的实验
public class VolatileTest {private static boolean flag = true;public static void main(String[] args) throws InterruptedException {new Thread(()->{while (flag) {}System.out.println("over");}).start();Thread.sleep(5);flag=false;}
}
此时,程序是不会有任何输出的,因为在更改flag为false后,线程栈帧内存储的变量没有被改变。
2.1 volatile确保了可见性
如下程序,简单增加volatile修饰后可以达到效果。
public class VolatileTest {private volatile static boolean flag = true;public static void main(String[] args) throws InterruptedException {new Thread(()->{while (flag) {}System.out.println("over");}).start();Thread.sleep(5);flag=false;}
}
运行结果:
注意:volatile一般修饰基础类型,尽量不要去修饰引用类型。
3. 一个指令乱序的实验
如果没有乱序执行,则只可能x和y 分别同时为1 ,或者是1 0 组合 或者是 0 1 组合。
一定不能出现x和y同时为0.
public class VolatileTest2 {static int a;static int b;static int y;static int x;public static void main(String[] args) throws InterruptedException {int count = 0;while (true) {a = 0;b = 0;y = 0;x = 0;Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {a = 1;x = b;}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {b = 1;y = a;}});t1.start();t2.start();t1.join();t2.join();count++;if (x == 0 && y == 0) {System.out.println("第"+count+"次");break;}}}
}
运行结果显示,出现了x和y同时为0。说明程序命令a = 1;x = b;
或b = 1;y = a
发生了乱序执行。
第183383次 (0,0)Process finished with exit code 0
总结
java使用volatile 修饰的变量,通过操作系统底层的锁总线(LOCK#)或者缓存一致性协议(MESI)实现可见性。
java对一个 volatile 域的写,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。
多线程系列在github上有一个开源项目,主要是本系列博客的实验代码。
https://github.com/forestnlp/concurrentlab
如果您对软件开发、机器学习、深度学习有兴趣请关注本博客,将持续推出Java、软件架构、深度学习相关专栏。
您的支持是对我最大的鼓励。
JAVA多线程基础篇 4、可见性、有序性与Volatile相关推荐
- JAVA多线程基础篇-关键字synchronized
1.概述 syncronized是JAVA多线程开发中一个重要的知识点,涉及到多线程开发,多多少少都使用过.那么syncronized底层是如何实现的?为什么加了它就能实现资源串行访问?本文将基于上述 ...
- Java多线程基础篇(02)-多线程的实现
为什么80%的码农都做不了架构师?>>> 1.概要 JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Call ...
- JAVA多线程基础篇-join方法的使用
1.概述 join()是Thread类中的一个方法,它的作用是将当前线程挂起,等待其他线程结束后再执行当前线程,即当前线程等待另一个调用join()方法的线程执行结束后再往下执行.通常用于在main主 ...
- java多线程基础篇第一篇-JMM
1.在开始多线程之前,我们先来聊聊计算机的缓存 计算机处理一个程序需要cpu处理器与存储设备的交互.但是在计算机发展的过程中,cpu处理器的处理速度不断提高,而存储设备的读写速度却没有得到与cpu同样 ...
- java多线程基础篇(二)java线程常见问题Thread Dump日志分析
线程常见问题 CPU占用率很高,响应很慢 CPU占用率不高,但响应很慢 线程出现死锁的情况 CPU占用率不高,但响应很慢 有的时候我们会发现CPU占用率不高,系统日志也看不出问题,那么这种情况下,我们 ...
- java多线程基础篇第二篇-volidate关键字
volidate关键字 转载于:https://www.cnblogs.com/code-star/p/11197708.html
- JAVA多线程设计模式篇 12、Thread-Specific Storage模式——给我个柜子
文章目录 1. ThreadLocal的使用示例 2. ThreadLocal的使用场景 2.1 线程隔离的数据库连接与事务 2.2 线程隔离的session会话 总结 多线程环境中即然共用资源这么困 ...
- Java多线程干货系列(1):Java多线程基础
转载自 Java多线程干货系列(1):Java多线程基础 前言 多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们来说极其重要,下面跟我一起开启本次的学 ...
- Java面试基础篇之集合
文章目录 你知道的集合都有哪些? 哪些集合是线程安全的? Collection 集合类和数组有什么不同? Collection和Collections有什么区别? 如何确保一个集合不能被修改? Lis ...
最新文章
- ISAIR2022征稿【中国・上海​, 2022年10月21-23日】
- ubuntu16.04安装torch
- 学ASP只需一小时!
- mysql 时间 设计模式_数据库时间设计模式
- 从零开始学ios开发(十三):Table Views(下)Grouped and Indexed Sections
- 理解 position:relative 与 position:absolute
- 《通信原理》复习笔记6----第六章数字基带传输系统(重中之重点+难上加难点)
- 安装duetdisplay遇到的问题
- php获取远程数据,教你如何用php实现LOL数据远程获取
- 【杂记】收藏的喜欢的句子
- Mac To Win
- 电子邮箱邮件怎么撤回,邮箱如何撤回邮件?
- ZooKeeper应用——解决分布式系统单点故障
- QQ网页登陆密码加密方式(农场、空间、WebQQ等通用)(网摘)
- 在“颜值至上”的互联网时代,我们是否需要美颜SDK?
- StringBuffer的理解
- Hinton曾授课,斯坦福首个Transformers专题讲座视频放出,NLP、CV和RL无所不包
- 【性能测试】如何完全卸载LoadRunner?
- java:上传微信临时文件的素材
- sap hana连接