当前位置: 首页 > news >正文

威海网站建设百度软件商店

威海网站建设,百度软件商店,威海市城乡建设局网站,教做凉拌菜的视频网站java基础—Volatile关键字详解 文章目录java基础—Volatile关键字详解并发编程的三大特性:volatile的作用是什么volatile如何保证有可见性volatile保证可见性在JMM层面原理volatile保证可见性在CPU层面原理可见性问题的例子volatile如何保证有序性单例模式使用volat…

java基础—Volatile关键字详解

文章目录

  • java基础—Volatile关键字详解
      • 并发编程的三大特性:
      • volatile的作用是什么
      • volatile如何保证有可见性
        • volatile保证可见性在JMM层面原理
        • volatile保证可见性在CPU层面原理
        • 可见性问题的例子
      • volatile如何保证有序性
        • 单例模式使用volatile保证有序性的例子
      • volatile为什么不能保证原子性

并发编程的三大特性:

原子性、可见性和有序性。只要有一条原则没有被保证,就有可能会导致程序运行不正确。volatile关键字 被用来保证可见性,即保证共享变量的内存可见性以解决缓存一致性问题。一旦一个共享变量被 volatile关键字 修饰,那么就具备了两层语义:内存可见性和禁止进行指令重排序。

  • 原子性:就是一个操作或多个操作中,要么全部执行,要么全部不执行。

    例如:账户A向账户B转账1000元,这个么过程涉及到两个操作,(1)A账户减去1000元 (2)B账户增加1000元。这么两个操作必须具备原子性。否则A账户钱少了,B账户没增加。

  • 有序性: 程序执行顺序按照代码先后顺序执行。

    处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致(指令重排),但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。(此处的结果一致指的是在单线程情况下)

    指令重排的理解:单线程侠,如果两个操作更换位置后,对后续操作结果没有影响,可以对这两个操作可以互换顺序。

  • 可见性: 可见性是指多线程共享一个变量,其中一个线程改变了变量值,其他线程能够立即看到修改的值。

    //线程1执行的代码
    int i = 0;
    i = 10;
    //线程2执行的代码
    j = i;

    CPU1执行线程1代码,CPU执行线程2代码。CPU读取i=0到CPU缓存中,修改i=10到自己缓存,还没更新到主存,此时CPU2读取的i还是主存中i=0,此时j会被赋值为0;

volatile的作用是什么

volatile是一个类型修饰符,JDK1.5之后,对其语义进行了增强。

  • 保证了不同线程之间对共享变量操作的可见性
  • 通过禁止编译器、CPU指令重排序和部分hapens-before规则,解决有序性

volatile如何保证有可见性


volatile保证可见性在JMM层面原理

volatile修饰的共享变量在执行写操作后,会立即刷回到主存,以供其它线程读取到最新的记录。

volatile保证可见性在CPU层面原理

volatile关键字底层通过lock前缀指令,进行缓存一致性的缓存锁定方案,通过总线嗅探和MESI协议来保证多核缓存的一致性问题,保证多个线程读取到最新内容。 lock前缀指令除了具有缓存锁定这样的原子操作,它还具有类似内存屏障的功能,能够保证指令重排的问题。

  • 被volatile修饰的变量在写操作生成汇编指令时,会多出Lock前缀指令,这个指令会引起CPU缓存刷回主存。
  • 刷回主存后,导致其他核心缓存了该内存地址的数据无效,通过缓存一致性协议(MESI)保证每个线程的数据是最新的。
  • 缓存一致性协议保证每个CPU核心通过嗅探在总线上传播的数据来检查自己的缓存是不是被修改,· 当 CPU 发现自己缓存行对应的内存地址被修改,会将当前 CPU 的缓存行设置成无效状态,重新从内存中把数据读到 CPU 缓存

可见性问题的例子

启动线程1和线程2,线程2设置stop=true。查看线程1是否会停止

public class TestVisibility {//是否停止 变量private static boolean stop = false;public static void main(String[] args) throws InterruptedException {new Thread(() -> {System.out.println("线程 1 正在运行...");while (!stop) ;System.out.println("线程 1 终止");}).start();//休眠 10 毫秒Thread.sleep(10);//启动线程 2, 设置 stop = truenew Thread(() -> {System.out.println("线程 2 正在运行...");stop = true;System.out.println("设置 stop 变量为 true.");}).start();}}

可见,线程1并不会停止,而是一直循环下去。这就是CPU缓存导致的一致性问题。

给stop加上volatile关键字,并运行,会发现线程1终止了

volatile如何保证有序性

  1. 内存屏障(Memory Barrier 又称内存栅栏,是一个 CPU 指令)禁止重排序

    Volatile关键字(JMM内存屏障),内存屏障也成为内存栏杆,是一个CPU指令,volatile修饰的变量,在读写操作前后都会进行屏障的插入来保证执行的顺序不被编译器等优化器锁重排序。

    内存屏障的功能有两个:(1)阻止屏障两边的指令重排、(2)刷新处理器缓存(保证内存可见性)image-20230204181902182

  2. 3 个 happens-before 规则实现:

    Happens-Before
    SR-133 提出了 happens-before 的概念,通过这个概念来阐述操作之间的内存可见性。如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须存在 happens-before 关系。这里提到的两个操作既可以是在一个线程之内,也可以是在不同线程之间。 与程序员密切相关的 happens-before 规则如下:

    • 程序顺序规则:一个线程中的每个操作,happens- before 于该线程中的任意后续操作。
    • 监视器锁规则: 对一个监视器锁的解锁,happens- before 于随后对这个监视器锁的加锁。
    • volatile 变量规则: 对一个 volatile 域的写,happens- before 于任意后续对这个 volatile 域的读。
    • 传递性: 如果 A happens- before B,且 B happens- before C,那么 A happens- before C。

    注意,两个操作之间具有 happens-before 关系,并不意味着前一个操作必须要在后一个操作之前执行!happens-before 仅仅要求前一个操作(执行的结果)对后一个操作可见,且前一个操作按顺序排在第二个操作之前(the first is visible to and ordered before the second)。happens- before 的定义很微妙

    img

单例模式使用volatile保证有序性的例子

为什么变量singleton之前需要加volatile

public class Singleton {public static volatile Singleton singleton;/*** 构造函数私有,禁止外部实例化*/private Singleton() {};public static Singleton getInstance() {if (singleton == null) {synchronized (singleton) {if (singleton == null) {singleton = new Singleton();}}}return singleton;}
}

先要了解对象的构造过程,实例化一个对象其实可以分为三个步骤:

  • 分配内存空间。
  • 初始化对象。
  • 将内存空间的地址赋值给对应的引用。

但是由于操作系统可以对指令进行重排序,所以上面的过程也可能会变成如下过程:

  • 分配内存空间。
  • 将内存空间的地址赋值给对应的引用。
  • 初始化对象

如果是这个流程,多线程环境下就可能将一个未初始化的对象引用暴露出来,从而导致不可预料的结果。因此,为了防止这个过程的重排序,我们需要将变量设置为volatile类型的变量

volatile为什么不能保证原子性

执行下面代码会发现,输出的并不是10000

public class atomiciVolitile {volatile  int  i = 0;public void addI(){i++;}public static void main(String[] args) throws InterruptedException {atomiciVolitile a=new atomiciVolitile();for (int i = 0; i < 10000; i++) {new Thread(() -> {try {Thread.sleep(10);//执行速度太快,没有起到并发作用,等待10毫秒} catch (InterruptedException e) {e.printStackTrace();}a.addI();}).start();}Thread.sleep(5000);System.out.println(a.i);}
}

image-20230208194634572

原因:i++其实是一个复合操作,包括三步骤:

  • 读取i的值。
  • 对i加1。
  • 将i的值写回内存。

volatile是无法保证这三个操作是具有原子性的,我们可以通过AtomicInteger或者Synchronized来保证+1操作的原子性。

想要了解更详细,请看这篇

java基础—java内存模型(JMM)CPU架构、缓存一致性、重排序、JMM的实现、JMM保证可见性、有序性问题的详解

http://www.ritt.cn/news/26375.html

相关文章:

  • 横岗做网站10条重大新闻事件
  • 怎么取消网站备案海口网站建设
  • asp access网站开发实例精讲百度营销搜索推广
  • 网站建设所学内容网络营销方式有哪些?
  • seo百度网站排名研究中心关键词首页优化企业网站模板建站
  • 健康南充app重庆公司网站seo
  • 做域名交易网站站长工具精品
  • 定制网站开发哪里好seo与sem的关系
  • 旅游景区网站建设方案关键词的作用
  • 网页制作教程百度云优化推广服务
  • 做篮球网站用的背景图百度软件应用市场
  • 产品策划书模板windows优化大师如何卸载
  • 网站建设 有限公司腾讯广点通广告投放平台
  • 旅游电子商务网站建设规划方案女孩短期技能培训班
  • 郴州网站建设公司百度收录怎么查询
  • 桂林网站优化选择桂林速优网络公司花都网站建设公司
  • 做律师推广的网站有哪些互联网营销方式有哪些
  • 好的网站建设网站网络营销成功案例分析
  • 温州优化售后宁波 seo排名公司
  • 福州建设局网站怎么做关键词优化排名
  • 做JAVA基础编程题什么网站好最新足球消息
  • 网站服务器参数2024年瘟疫大爆发
  • 怎么做百度自己的网站空间抖音宣传推广方案
  • 网站上的洗衣液瓶子做花瓶怎么材质黑帽seo培训大神
  • 政府门户网站建设的工作总结网络营销策划推广方案
  • 东莞长安网站设计百度seo快排软件
  • 多语种网站制作温州seo博客
  • 淘客网站推广怎么做seo的工作内容主要包括
  • 甘肃网站怎样备案自动引流推广软件
  • 备案个人网站免费网站建设哪家好