概念解释:

原子性(Atomicity)
由 Java 内存模型来直接保证的原子性变量操作包括 read、load、assign、use、store 和 write。大致可以认为基本数据类型的操作是原子性的。同时 lock 和 unlock 可以保证更大范围操作的原子性。而 synchronize 同步块操作的原子性是用更高层次的字节码指令 monitorenter 和 monitorexit 来隐式操作的。

可见性(Visibility)
是指当一个线程修改了共享变量的值,其他线程也能够立即得知这个通知。主要操作细节就是修改值后将值同步至主内存(volatile 值使用前都会从主内存刷新),除了 volatile 还有 synchronize 和 final 可以保证可见性。同步块的可见性是由“对一个变量执行 unlock 操作之前,必须先把此变量同步会主内存中( store、write 操作)”这条规则获得。而 final 可见性是指:被 final 修饰的字段在构造器中一旦完成,并且构造器没有把 “this” 的引用传递出去( this 引用逃逸是一件很危险的事情,其他线程有可能通过这个引用访问到“初始化了一半”的对象),那在其他线程中就能看见 final 字段的值。

有序性(Ordering)
如果在被线程内观察,所有操作都是有序的;如果在一个线程中观察另一个线程,所有操作都是无序的。前半句指“线程内表现为串行的语义”,后半句是指“指令重排”现象和“工作内存与主内存同步延迟”现象。Java 语言通过 volatile 和 synchronize 两个关键字来保证线程之间操作的有序性。volatile 自身就禁止指令重排,而 synchronize 则是由“一个变量在同一时刻指允许一条线程对其进行 lock 操作”这条规则获得,这条规则决定了持有同一个锁的两个同步块只能串行的进入。

1、原子性

原子性是指一个操作是不可中断的,要么全部执行成功要么全部执行失败,有着“同生共死”的感觉。及时在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程所干扰。我们先来看看哪些是原子操作,哪些不是原子操作,有一个直观的印象:

int a = 10; //1
a++; //2
int b=a; //3
a = a+1; //4

上面这四个语句中只有第1个语句是原子操作,将10赋值给线程工作内存的变量a,而语句2(a++),实际上包含了三个操作:1. 读取变量a的值;2:对a进行加一的操作;3.将计算后的值再赋值给变量a,而这三个操作无法构成原子操作。对语句3,4的分析同理可得这两条语句不具备原子性。当然,java内存模型中定义了8中操作都是原子的,不可再分的。

上面的这些指令操作是相当底层的,可以作为扩展知识面掌握下。那么如何理解这些指令了?比如,把一个变量从主内存中复制到工作内存中就需要执行read,load操作,将工作内存同步到主内存中就需要执行store,write操作。注意的是:java内存模型只是要求上述两个操作是顺序执行的并不是连续执行的。也就是说read和load之间可以插入其他指令,store和writer可以插入其他指令。比如对主内存中的a,b进行访问就可以出现这样的操作顺序:read a,read b, load b,load a

由原子性变量操作read,load,use,assign,store,write,可以大致认为基本数据类型的访问读写具备原子性(例外就是long和double的非原子性协定)

synchronized:

上面一共有八条原子操作,其中六条可以满足基本数据类型的访问读写具备原子性,还剩下lock和unlock两条原子操作。如果我们需要更大范围的原子性操作就可以使用lock和unlock原子操作。尽管jvm没有把lock和unlock开放给我们使用,但jvm以更高层次的指令monitorenter和monitorexit指令开放给我们使用,反应到java代码中就是---synchronized关键字,也就是说synchronized满足原子性

上面一共有八条原子操作,其中六条可以满足基本数据类型的访问读写具备原子性,还剩下lock和unlock两条原子操作。如果我们需要更大范围的原子性操作就可以使用lock和unlock原子操作。尽管jvm没有把lock和unlock开放给我们使用,但jvm以更高层次的指令monitorenter和monitorexit指令开放给我们使用,反应到java代码中就是---synchronized关键字,

也就是说volatile
我们先来看这样一个例子:

public class VolatileExample {private static volatile int counter = 0;public static void main(String[] args) {for (int i = 0; i < 10; i++) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 10000; i++)counter++;}});thread.start();}try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(counter);}
}

开启10个线程,每个线程都自加10000次,如果不出现线程安全的问题最终的结果应该就是:10*10000 = 100000;可是运行多次都是小于100000的结果,问题在于 volatile并不能保证原子性,在前面说过counter++这并不是一个原子操作,包含了三个步骤:1.读取变量counter的值;2.对counter加一;3.将新值赋值给变量counter。如果线程A读取counter到工作内存后,其他线程对这个值已经做了自增操作后,那么线程A的这个值自然而然就是一个过期的值,因此,总结果必然会是小于100000的。

如果让volatile保证原子性,必须符合以下两条规则:

  1. 运算结果并不依赖于变量的当前值,或者能够确保只有一个线程修改变量的值;
  2. 变量不需要与其他的状态变量共同参与不变约束

3. 有序性

synchronized

synchronized语义表示锁在同一时刻只能由一个线程进行获取,当锁被占用后,其他线程只能等待。因此,synchronized语义就要求线程在访问读写共享变量时只能“串行”执行,因此synchronized具有有序性

volatile

在java内存模型中说过,为了性能优化,编译器和处理器会进行指令重排序;也就是说java程序天然的有序性可以总结为:如果在本线程内观察,所有的操作都是有序的;如果在一个线程观察另一个线程,所有的操作都是无序的。在单例模式的实现上有一种双重检验锁定的方式(Double-checked Locking)。代码如下:

4. 可见性

可见性是指当一个线程修改了共享变量后,其他线程能够立即得知这个修改。通过之前对synchronzed内存语义进行了分析,当线程获取锁时会从主内存中获取共享变量的最新值,释放锁的时候会将共享变量同步到主内存中。从而,synchronized具有可见性。同样的在volatile分析中,会通过在指令中添加lock指令,以实现内存可见性。因此, volatile具有可见性

5. 总结

通过这篇文章,主要是比较了synchronized和volatile在三条性质:原子性,可见性,以及有序性的情况,归纳如下:

synchronized: 具有原子性,有序性和可见性
volatile:具有有序性和可见性

原子性、有序性和可见性解释相关推荐

  1. 并发编程:原子性问题,可见性问题,有序性问题。

    以下是本文的目录大纲: 一.内存模型的相关概念 二.并发编程中的三个概念 三.Java内存模型 一.内存模型的相关概念 大家都知道,计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中, ...

  2. 三大性质总结:原子性,有序性,可见性

    转载自:https://www.jianshu.com/p/cf57726e77f2 1. 三大性质简介 在并发编程中分析线程安全的问题时往往需要切入点,那就是两大核心:JMM抽象内存模型以及happ ...

  3. 线程安全问题的本质详解: 原子性、有序性、可见性

    内容导航 volatile的作用 什么是可见性 volatile源码分析 一.volatile的作用 在多线程中,volatile和synchronized都起到非常重要的作用,synchronize ...

  4. 面试官:你对多线程熟悉吗,谈谈线程安全中的原子性,有序性和可见性?

    作者:a60782885 blog.csdn.net/a60782885/article/details/77803757 注,本篇只是解析基本概念,用作面试应答,非深入 对于Java并发编程,一般来 ...

  5. php三要素,并发编程三要素:原子性,有序性,可见性

    并发编程三要素 **原子性:**一个不可再被分割的颗粒.原子性指的是一个或多个操作要么全部执行成功要么全部执行失败. 有序性: 程序执行的顺序按照代码的先后顺序执行.(处理器可能会对指令进行重排序) ...

  6. treemap怎么保证有序_你对多线程熟悉吗,谈谈线程安全中的原子性,有序性和可见性?...

    作者:a60782885 blog.csdn.net/a60782885/article/details/77803757 注,本篇只是解析基本概念,用作面试应答,非深入 对于Java并发编程,一般来 ...

  7. 重点知识学习(8.2)--[JMM(Java内存模型),并发编程的可见性\原子性\有序性,volatile 关键字,保持原子性,CAS思想]

    文章目录 1.JMM(Java Memory Model) 2.并发编程的可见性 3.并发编程的有序性 4.并发编程的原子性 5.volatile 关键字 6.保持原子性: 加锁,JUC原子类 加锁 ...

  8. MESI协议为何会引发 有序性、可见性的问题

    引发可见性 case1:处理器将数据写入到写缓冲器,发送invalidate消息到总线bus,就认为成功了.此时数据并未到高速缓存中. case2:处理器1在嗅探到invalidate之前,就去读取高 ...

  9. CAS(比较并交换)学习CAS实现原子性+volatile实现可见性,cas与synchronized比较的优缺点

    1.CAS底层原理? 自旋锁(cas思想)+unsafe类,保证原子性靠的是unsafe类 1.首先可以看到: atomicInteger.getAndIncrement(); getAndIncre ...

最新文章

  1. python装饰器实例-python装饰器实例大详解
  2. mysql算法优化原则_Mysql优化原则_小表驱动大表IN和EXISTS的合理利用
  3. zipsys驱动签名工具_全球首发 300系列主板USB WIN7 64位驱动 SMXDIY
  4. python中函数参数_Python函数的参数
  5. 学python需要什么基础-要学 Python 需要怎样的基础?
  6. 【IJCAI2019】中国团队占 38%,北大南大榜上有名
  7. Git 可视化管理工具 - Sourcetree 使用指南
  8. 手机驱动工程师门,准备转行了吗
  9. linux上python升级_Linux下安装升级python
  10. log10/log2--求常用对数/以2为底的对数
  11. 电子邮件签名模板_15个网站下载免费的电子邮件通讯模板
  12. FusionGAN:一种生成式红外与可见光图像融合对抗网络
  13. arcgis中去除图层白底,并导出透明底tif
  14. spring配置bean
  15. mysql集群重启失败_Mysql集群重启失败
  16. 08音视频设备类、09信息技术设备、16电信终端设备CCC认证流程费用及周期
  17. PIC单片机之中断程序
  18. 基于java的SFTP工具类
  19. 【笔记】DenseTNT:End-to-end Trajectory Prediction from Dense Goal Sets
  20. Python使用tkinter开发一个简单的参数计算软件模板,可用于设计估算,制造业算料,各种包含参数变量的简单计算

热门文章

  1. [oc学习日记]代理模式
  2. H5 自定义数据属性
  3. 作着玩:登录页(纯css,不支持ie9以下)
  4. python怎么让输出居中_python格式化输出字符串居中
  5. python的matplotlib库怎么安装_为Python安装matplotlib库
  6. android个人中心界面_Android 机也能用上你熟悉的浏览器插件,这些浏览器不可错过...
  7. 添加空值_Python基础 | 0x8空值、布尔类型、数字类型
  8. ce修改服务器的数据库,数据库服务器的调优步骤
  9. mysql function_MySQL基础函数——数学函数详解
  10. Windows核心编程学习笔记