文章目录

  • 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相关推荐

  1. JAVA多线程基础篇-关键字synchronized

    1.概述 syncronized是JAVA多线程开发中一个重要的知识点,涉及到多线程开发,多多少少都使用过.那么syncronized底层是如何实现的?为什么加了它就能实现资源串行访问?本文将基于上述 ...

  2. Java多线程基础篇(02)-多线程的实现

    为什么80%的码农都做不了架构师?>>>    1.概要 JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Call ...

  3. JAVA多线程基础篇-join方法的使用

    1.概述 join()是Thread类中的一个方法,它的作用是将当前线程挂起,等待其他线程结束后再执行当前线程,即当前线程等待另一个调用join()方法的线程执行结束后再往下执行.通常用于在main主 ...

  4. java多线程基础篇第一篇-JMM

    1.在开始多线程之前,我们先来聊聊计算机的缓存 计算机处理一个程序需要cpu处理器与存储设备的交互.但是在计算机发展的过程中,cpu处理器的处理速度不断提高,而存储设备的读写速度却没有得到与cpu同样 ...

  5. java多线程基础篇(二)java线程常见问题Thread Dump日志分析

    线程常见问题 CPU占用率很高,响应很慢 CPU占用率不高,但响应很慢 线程出现死锁的情况 CPU占用率不高,但响应很慢 有的时候我们会发现CPU占用率不高,系统日志也看不出问题,那么这种情况下,我们 ...

  6. java多线程基础篇第二篇-volidate关键字

    volidate关键字 转载于:https://www.cnblogs.com/code-star/p/11197708.html

  7. JAVA多线程设计模式篇 12、Thread-Specific Storage模式——给我个柜子

    文章目录 1. ThreadLocal的使用示例 2. ThreadLocal的使用场景 2.1 线程隔离的数据库连接与事务 2.2 线程隔离的session会话 总结 多线程环境中即然共用资源这么困 ...

  8. Java多线程干货系列(1):Java多线程基础

    转载自  Java多线程干货系列(1):Java多线程基础 前言 多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们来说极其重要,下面跟我一起开启本次的学 ...

  9. Java面试基础篇之集合

    文章目录 你知道的集合都有哪些? 哪些集合是线程安全的? Collection 集合类和数组有什么不同? Collection和Collections有什么区别? 如何确保一个集合不能被修改? Lis ...

最新文章

  1. ISAIR2022征稿【中国・上海​, 2022年10月21-23日】
  2. ubuntu16.04安装torch
  3. 学ASP只需一小时!
  4. mysql 时间 设计模式_数据库时间设计模式
  5. 从零开始学ios开发(十三):Table Views(下)Grouped and Indexed Sections
  6. 理解 position:relative 与 position:absolute
  7. 《通信原理》复习笔记6----第六章数字基带传输系统(重中之重点+难上加难点)
  8. 安装duetdisplay遇到的问题
  9. php获取远程数据,教你如何用php实现LOL数据远程获取
  10. 【杂记】收藏的喜欢的句子
  11. Mac To Win
  12. 电子邮箱邮件怎么撤回,邮箱如何撤回邮件?
  13. ZooKeeper应用——解决分布式系统单点故障
  14. QQ网页登陆密码加密方式(农场、空间、WebQQ等通用)(网摘)
  15. 在“颜值至上”的互联网时代,我们是否需要美颜SDK?
  16. StringBuffer的理解
  17. Hinton曾授课,斯坦福首个Transformers专题讲座视频放出,NLP、CV和RL无所不包
  18. 【性能测试】如何完全卸载LoadRunner?
  19. java:上传微信临时文件的素材
  20. sap hana连接

热门文章

  1. 浏览器的onload事件
  2. 程序员学习指南_程序员管理压力指南
  3. [生存志] 第79节 国语述诸国
  4. JavaScript实现猜数字游戏(猜一位和四位数字)
  5. 网络使用工具HttpWatch的使用方法
  6. excel中条形图的条目排序与逆序
  7. 用编程Python赚钱的5个方法
  8. 6轮面试辛苦拿到阿里Android开发offer,却从22k降到15k,在逗我
  9. 青少年编程究竟应该从什么语言学起?
  10. 顺丰笔试-赏金猎人2020-08-20(参考时间最大利益)