介绍完Java内存模型的相关操作与规则,会发现其主要就是围绕着在并发过程中如何处理原子性、可见性和有序性这三个特征来建立的,这也是并发编程最核心的核心概念。
可见性
可见性是指在并发环境下,当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。
在多核处理器的架构中,由于CPU缓存的存在,当某一核心修改了共享变量的值,并不一定会立即刷新回主内存,而且就算立即刷新回了主内存,其他同样使用该共享变量的线程也不一定会立刻感知到其数据的变化从而重新从主内存加载共享变量。
在Java中,有如下几种手段可以保证并发可见性(其实说到底最底层都是通过设置内存屏障来实现的)。
1. volatile关键字。 volatile修饰的变量保证新值能够立即同步到主内存,以及每次使用前都立即从主内存刷新。
2. synchronized以及Lock显示锁。根据JMM内存交互规则:对一个变量执行unlock操作之前,必须先把此变量同步回主内存,对一个变量执行lock操作之前,将会清空工作内存中该变量的值,重新从主内存加载。
3. final关键字。被final修饰的字段在构造器中一旦被初始化完成并且在初始化的过程中没有把“this”的引用传递出去,那么其他线程就能看的final字段的值。
4. 原子变量。类似AtomicInteger这样的原子变量,其实内部借助了volatile关键字。
5. 其他一切遵循happens-before先行发生原则的操作过程,当然其实上面提到的所有手段都属于此类。
有序性
有序性其实有以下几层语义:
1. 单线程内部数据不相关的操作由于指令重排序导致的问题。
在 Java内存模型JMM之四volatile关键字 章节中关于有序性描述的内容,初始化完成之后的标识变量的重排序就会导致另一个线程的执行出现错误。针对该问题可以使用Volatile关键字解决。
2. 多线程并发执行的无序性。
多线程对于临界区代码的无序执行就该该问题的体现。使用synchronized关键字、Lock显式锁对临界区进行加锁,即可时多线程串形执行临界去代码。
3. 由于修改共享变量之后的可见性不能保证导致的程序执行结果无法达到预期的变相的有序性问题。
笼统地说,这是“工作内存或者处理器缓存与主内存同步延迟”现象导致的一系列问题,严格来说第1条也属于此类,只是此处的描述的问题更广泛。满足happens-before先行发生原则的操作将会是这类问题的统一判断标准,在不能通过先行发生原则推导出有明确的有序性来的情况下,可以使用synchronized关键字、Lock显式锁解决所有问题。
原子性
原子性是指某个(些)操作在语意上是原子的。即一个操作或者多个操作要么全部执行并且执行的过程不会被任何因素打断(包含CPU暂停调度),要么就都不执行。 如果一个操作是原子性的, 那么在多线程环境下, 就不会出现在操作的过程中变量被其他线程修改等奇怪的问题。比如对单个变量的读或者写操作是原子操作,因为读或者写这样的操作在计算机底层指令级是原子的,对某个内存地址的指令操作在同时时刻只能被一个指令独享。另外根据上一章我知道CAS操作也是原子操作。
在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作。这看起来简单,其实理解起来并不是那么容易。看下面一个例子:
x = 10; //语句1 y = x; //语句2 x++; //语句3 x = x + 1; //语句4以上4个语句,其实只有语句1是原子操作,它直接数值10写入到工作内存中。要保证非原子的操作具有原子性,可以使用synchronized关键字、Lock显式锁、CAS操作来达到整个操作的原子性。
非原子协定
上面提到对基本类型的变量的读写操作是原子操作,但是对于64位的数据类型long和double却并不一定满足。因为在JMM内存模型中特别定义了一条宽松的规定:允许JVM虚拟机将没有被volatile修饰的64位数据的读写操作划分为两次32位的操作两进行。
所以对 long和double类型的变量的读写操作不保证一定是原子操作,这就是所谓的long和double的非原子协定。所以当多个线程同时对一个并未声明为volatile的long或者double变量进行读取或者修改操作,那么某些线程将可能会读取到一个既非原值,也不是其他线程修改值的代表了“半个变量”的数值,当然这种情况非常罕见。因为大多数平台下的JVM虚拟机都还是选择了把64位数据的读与写操作实现为原子操作的。
综上总结
synchronized关键字、Lock对象同时支持原子性、有序性和可见性。
volatile关键字只支持可见性和有序性,并不保证原子性。
CAS (原子性)+ Volatile关键字(有序性、可见性)也能实现对单个变量操作的原子性、有序性和可见性。
相关推荐
深入理解并发可见性、有序性、原子性与JMM内存模型深入理解并发可见性、有序性、原子性与JMM内存模型深入理解并发可见性、有序性、原子性与JMM内存模型深入理解并发可见性、有序性、原子性与JMM内存模型深入理解并发...
"并发二:原子性、可见性、有序性" 本文主要讲解了Java内存模型(JMM)中关于并发编程的三个基本概念:原子性、可见性和有序性。 原子性 原子性是指一个操作不会被线程调度机制打断,一旦开始,就一直运行到结束...
多线程:可见性、有序性、原子性.xmind
Java多线程编程中,原子性、可见性和有序性是三个关键的概念,它们确保了多线程环境下的正确性。 1. 原子性(Atomicity) 原子性指的是一个操作不可被中断,要么全部执行,要么完全不执行。在Java中,非原生类型的...
根据提供的文档信息,本文将详细解析并发编程中的关键概念——原子性、可见性及有序性,并结合Java内存模型(JMM)来深入理解这些概念。同时,我们也会通过具体示例来探讨这些问题在实际编程中的应用。 ### 一、并发...
然而,并发编程也带来了一系列挑战,如线程安全、可见性、原子性和有序性问题。 **并发理论基础**: 并发编程的核心在于多线程,允许多个执行单元在同一时间执行任务,以充分利用系统资源。在Java中,通过创建...
本文将深入探讨JMM中的三个核心概念:原子性、有序性和可见性。 ### 1. 原子性(Atomicity) 原子性是指一个操作或多个操作被视为一个不可分割的整体,即这些操作要么全部完成,要么都不完成。在Java中,为了保证...
在Java并发编程中,原子性和可见性是两个关键的概念,它们直接影响着多线程环境下的程序行为和数据一致性。理解这两个概念对于编写高效且线程安全的代码至关重要。 首先,让我们详细探讨一下原子性。原子性指的是一...
本章将通过“有福同享,有难同当—原子性”这一主题,深入探讨并发编程中的核心概念——原子性,以及与之相关的线程安全和并发编程的三大特性。 并发编程的三大特性包括原子性、可见性和有序性。这些特性是理解和...
本文将探讨三个关键概念:过期数据、锁的可见性以及Volatile关键字的作用。首先,我们来看一下“过期数据”这一问题。 1. 过期数据 在并发环境中,当多个线程共享数据时,如果没有适当的同步机制,一个线程修改的...
巴耳末公式描述了氢原子光谱中可见光区的谱线,其背后的物理机制是电子从高能级向低能级跃迁时释放的光子能量与两个能级之间的能量差成正比。 此外,试题可能会涉及到量子力学中的不确定性原理,例如:“海森堡不...
Java中的Volatile关键字详解是Java中的一种关键字,用于保证线程之间的可见性、原子性和有序性。下面是对Java中的Volatile关键字详解的知识点总结: 一、基本概念 1. 可见性:可见性是一种复杂的属性,因为可见性...
Java多线程编程中,可见性和有序性是两个至关重要的概念,它们直接影响到并发执行的正确性和线程安全。在Java内存模型(JMM)中,这些特性得到了明确的规定。 **可见性**指的是一个线程对共享变量的修改能够被其他...
非原子子系统,也称为封装子系统,与原子子系统类似,也是由多个 blocks 组成,但其区别在于其内部结构在模型视图中是可见的。非原子子系统的边界框内显示了所有包含的 blocks,使得用户无需打开子系统就能看到其...
java 并发中的原子性与可视性实例详解 Java 并发中的原子性与可视性是两个非常重要的概念,它们都是Java 并发编程中需要注意的关键要素。下面我们将从概念、实例和关系三个方面来详细介绍Java 并发中的原子性与可视...
在这个领域,"原子性"是并发控制的三大特性之一,另外两个是"可见性"和"有序性"。原子性保证了某个操作不会被其他线程中断,从而避免了竞态条件。在Java中,`Atomic`类提供了一种轻量级的、非阻塞的原子性操作实现,...
2. **光子能量与可见光的关系**:要使处于基态的氢原子被激发并辐射出可见光光子,氢原子必须吸收至少12.09 eV的能量跃迁到n=3的能级,然后跃迁回较低能级时释放出可见光光子。 3. **能级跃迁与光谱**:氢原子从...
原子光谱的观测与分析是现代光学分析技术中的重要组成部分,通过实验,我们不仅可以观察到原子发射和吸收光谱的独特性,还能深入理解电子在原子内部的行为。本文将结合原子发射光谱与吸收光谱的观测与分析实验,系统...