首页 > 编程语言 >Java的可见性和原子性

Java的可见性和原子性

时间:2023-06-02 21:36:29浏览次数:47  
标签:Java 变量 synchronized 可见 原子 volatile 内存 线程




1. 工作内存和主内存

  • 所有的变量都存在主内存中(一份)
  • 每个线程有自己独立的工作内存(主内存中该变量的一份拷贝)

2. 可见性和共享变量

  • 可见性:一个共享变量的值能够及时地被其他线程看到
  • 共享变量:如果一个变量在多个线程的工作内存中都存在拷贝,那么它就是这几个线程的共享变量

下图可以反映以上说明:

3. 约束

  • 线程对共享变量的操作只能在自己的工作内存中进行,不能直接修改主内存
  • 不同线程之间无法访问其他线程工作内存中的变量,必须通过主内存来访问其他线程工作内存中的变量

4. 共享变量可见性的原理

线程1对工作内存1的修改能及时让线程2及时可到,就是可见了。

4.1 可见性的步骤

  1. 将工作内存1中更新后的值写入主内存
  2. 主内存将最新的共享变量的值写入工作内存2

4.2 保证可见性的必要条件

  • 工作内存更新的共享变量值要及时写入主内存
  • 主内存及时更新共享变量的值写入工作内存2

synchronized实现可见性

5.1 synchronized实现的内容

  1. 原子性(同步)
  2. 可见性

5.2 JMM关于synchronized 的规定

  1. 解锁前必须把工作内存最新值写入主内存
  2. 加锁前清空工作内存的共享变量值,从主内存更新最新值

5.3 synchronized互斥代码的过程

  1. 获得互斥锁
  2. 清空工作内存
  3. 从主内存拷贝最新变量复制到自己的工作内存
  4. 执行代码
  5. 将更改的共享变量值写入主内存
  6. 释放互斥锁

6 volatile实现可见性

首先在说volatile实现可见性方法之前说一下编译重排序

6.1 重排序

编译器或处理器为了提高性能做优化,调整了执行顺序使得和书写顺序不同

6.2 as if serial语义

无论如何排序,程序执行结果与代码按照书写顺序执行的结果一致。在单线程情况下,遵循as if serial语义

volatile通过内存屏障和禁止重排序优化来实现可见性

  • 写操作后加入store屏障指令:写之后强行将更新的值写入主内存
  • 读操作前加入load屏障指令:读之前及时从主内存更新最新值

6.3 volatile无法实现原子性

  • 一般来说自增操作无法保证其原子性

解决方案:

  1. 同步块
  2. ReentrantLock(java.util.concurrent.locks) 3.AtomicInteger(java.util.concurrent.atomic)

多线程中安全使用volatile需要同时满足以下2点

  1. 对变量的写入操作不依赖当前值(比如自增、自减等操作;)
  2. 不能包含在一个不变式中(比如low<up,永远成立的)

7 synchronized和volatile的比较

  1. volatile不需要加锁,更加轻量级,不会阻塞线程
  2. 从内存可见性角度,volatile变量读等价于加锁,写等价于解锁
  3. synchronized可以同时保证原子性和可见性,而volatile一般来说只能保证可见性
  4. volatile需要注意使用的风险
  5. volatile仅能使用在变量级别,synchronized则可以使用在变量,方法.
  6. volatile标记的变量不会被编译器优化,而synchronized标记的变量可以被编译器优化 原文地址:


标签:Java,变量,synchronized,可见,原子,volatile,内存,线程
From: https://blog.51cto.com/u_16131764/6405097

相关文章

  • 构建服务器集群感知的 Java 应用程序
    ApacheZooKeeper和LinkedInProjectNorbert在分布式企业Java应用程序中简化服务器组协调服务器集群对于高度可扩展的Java企业级应用程序开发已司空见惯,但是应用程序级别的服务器集群感知目前并不属于JavaEE的一部分。在本文中,MukulGupta和PareshPaladiya向您介......
  • Google Java编程风格指南
    作者:Hawstein目录前言源文件基础源文件结构格式命名约定编程实践Javadoc后记前言这份文档是GoogleJava编程风格规范的完整定义。当且仅当一个Java源文件符合此文档中的规则,我们才认为它符合Google的Java编程风格。与其它的编程风格指南一样,这里所讨论的不仅仅是编码格......
  • Java编译与反编译
    Java编译与反编译什么是编译利用编译程序从源语言编写的源程序产生目标程序的过程。用编译程序产生目标程序的动作。编译就是把高级语言变成计算机可以识别的2进制语言,计算机只认识1和0,编译程序把人们熟悉的语言换成2进制的。编译程序把一个源程序翻译成目标程序的工作过程......
  • 40基于java的美食菜谱分享系统设计与实现
    本章节给大家带来一个基于java的美食菜谱分享系统设计与实现,餐饮分享平台设计与实现,可用于美食在线分享平台,作为世界各地爱好美食的人们的桥梁,为其创造一个氛围好的平台,促进美食世界的文化交流。该系统是一个供商家或者个人推荐美食的网站,网站不支持交易仅供分享。引言在21世......
  • [Javascript] Microtasks exec order
    button.addEventListener('click',(event)=>{console.log('listener1')queueMicrotask(()=>{console.log('microtask')})})button.addEventListener('click&#......
  • Java中double类型四舍五入的方法总结
    代码:doublea=13.245; //方法一:BigDecimalbd=newBigDecimal(a);Doubleb=bd.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue();System.out.println("方法一:"+b);//方法二:DoublemyValue=newBigDecimal(a).setScale(2,BigDecimal.ROUND_HALF_UP)......
  • Java 计算数学表达式(字符串解析求值工具)
    Java字符串转换成算术表达式计算并输出结果,通过这个工具可以直接对字符串形式的算术表达式进行运算,并且使用非常简单。这个工具中包含两个类Calculator和ArithHelperCalculator代码如下:importjava.util.Collections;importjava.util.Stack;/***算数表达式求值*......
  • java单例模式几种实现方式
    1、饿汉式(线程安全,调用效率高,但是不能延时加载):publicclassImageLoader{privatestaticImageLoaderinstance=newImageLoader;privateImageLoader(){}publicstaticImageLoadergetInstance(){returninstance;}}一上来就把单例对象创建出来了,要用的时候直......
  • Java8 Lambda表达式
    学习资料:https://www.bilibili.com/video/BV1ci4y1g7qD/?spm_id_from=333.337.search-card.all.click&vd_source=46d50b5d646b50dcb2a208d3946b1598......
  • IDEA集成Java性能分析神器JProfiler
    阅读文本大概需要10分钟。《eclipse集成Java性能分析神器JProfiler》讲解了eclipse集成Jprofiler,这篇讲解一下IDEA如何集成JProfiler。1、在JProfiler中配置IDEA选择IDEA2019这里并不同于Eclipse选择Eclipse的安装目录。IDEA选择的是配置目录,啥为配置目录了呢?其实就是在配置JProfi......