JMM篇之木秀于林

JMM

1.什么是JMM?
JMM:java内存模型(Java Memory model)
2.它的作用?
(1)缓存一致性协议,用于数据读写的规则。
(2)解决共享对象可见性:volilate(保持对象一致性)
(3)JMM定义了线程工作内存和主存之间的抽象关系。
线程之间的共享变量存储主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存中存储了该线程以读/写共享变量的副本(拷贝过来的)。在这里插入图片描述
3.JMM的八种指令
read(读取):把一个变量的值从主内存传输到工作内存中。
load(载入):在read执行之后,把read的值放入工作内存的变量副本中。
use(使用):把工作内存中一个变量的值传递给执行引擎。
assign(赋值):把一个从执行引擎接受到的值赋值给工作内存中的变量。
store(存储):把工作内存的一个变量值传递到主内存中。
write(写入):在store执行之后,把store得到的值放入主内存的变量中。
lock(锁定):作用于主内存变量。
unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
4.JMM八种指令的使用规则
(1)不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write。
(2)不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存。
(3)不允许一个线程将没有assign的数据从工作内存同步回主内存。
(4)一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前,必须经过assign和load操作。
(5)一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁。
(6)如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值。
(7)如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量。
(8)对一个变量进行unlock操作之前,必须把此变量同步回主内存。
5.JMM的三大特性
(1)原子性
指在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行。
JMM保证了read、load、use、assign、store、write、lock、unlock操作具有原子性。
(2)可见性
指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。
JMM是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值来实现可见性的。
(3)有序性
即程序执行的顺序按照代码的先后顺序执行。
在本线程内观察,所有操作都是有序的。在一个线程观察另一个线程,所有操作都是无序的(原因:发生了指令重排序)在 Java 内存模型中,允许编译器和处理器对指令进行重排序,重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
6.实现可见性的方式
(1)volatile,会 强制 将该变量自己和当时其他变量的状态都 刷出缓存 。
(2)synchronized,对一个变量执行 unlock 操作之前,必须把变量值同步回主内存。
(3)final,被 final 关键字修饰的字段在构造器中一旦初始化完成,并且没有发生 this 逃逸(其它线程通过 this 引用访问到初始化了一半的对象),那么其它线程就能看见 final 字段的值。
7.保证有序性的方式
(1)volatile 关键字通过添加内存屏障的方式来禁止指令重排,即重排序时不能把后面的指令放到内存屏障之前。
(2) synchronized 来保证有序性,它保证每个时刻只有一个线程执行同步代码,相当于是让线程顺序执行同步代码。

### JMM 的基本概念 Java 内存模型(JMM, Java Memory Model)是 Java 虚拟机的一部分,用于定义多线程程序在共享内存中的交互规则。它的核心目标在于为线程间操作提供一致的行为规范,确保多线程环境下数据的一致性和可见性[^1]。 JMM 不仅是一个抽象的概念框架,还涉及具体的实现细节,比如如何利用硬件特性(如缓存一致性协议、内存屏障等),从而保障多线程程序的正确运行[^2]。 --- ### JMM 的主要原理 #### 1. **内存分区结构** JMM 定义了一个抽象的内存结构,其中主要包括主内存(Main Memory)和工作内存(Working Memory)。 - 主内存存储的是实例对象及其字段的实际值。 - 工作内存则是每个线程私有的区域,保存该线程使用的变量副本。当线程读取某个变量时,会先从主内存加载到自己的工作内存;写入时,则需将更新后的值刷新回主内存[^3]。 这种设计使得不同线程之间无法直接访问彼此的工作内存,而必须通过主内存进行通信。 #### 2. **三大特性** ##### (1) **原子性** 指不可分割的操作,在同一时刻只能由一个线程完成。然而需要注意的是,某些看似简单的操作可能并非真正意义上的原子操作。例如 `i++` 实际上包含了三个步骤:读取 i 的当前值、计算新值并将其写回。如果多个线程同时执行此语句,就可能出现竞态条件[^4]。 为了保证复杂操作的原子性,通常借助锁机制(如 synchronized 或 ReentrantLock)或者无锁算法(如 CAS 循环比较交换技术)来实现。 ##### (2) **可见性** 指的是当一个线程修改了某共享变量后,其他线程能够立即感知这一变化的能力。由于现代计算机体系架构中存在高速缓存层,可能导致最近一次更改尚未同步至主存便被另一个 CPU 核心所获取的情况发生。为此,JMM 提供了一些关键字支持,像 volatile 就是用来强制指定每次对该标记位属性的访问都必须直接作用于主存之上,而不是依赖本地拷贝版本。 ##### (3) **有序性** 即使源码层面按照既定顺序书写逻辑流程,但在实际编译优化过程中仍可能发生指令重排现象——即调整原本应该遵循先后次序的动作序列以便提升效率。不过这往往会造成意想不到的结果特别是在跨线程协作场景下尤为明显。因此引入 happens-before 关系作为约束手段之一,明确规定哪些情形之下允许打破常规排列规律而不影响最终效果评估标准。 以下是几个典型的 happens-before 规则: - 程序顺序规则:单一线程内的动作保持原有声明位置关系; - 监视器锁定规则:解锁前发生的事件必定早于后续加锁行为之前; - Volatile 变量规则:针对同一个 volatile 类型成员项而言,前面对其所做的任何改动必然优先反映给后来查询者看到的内容; - 线程启动/终止规则:新建子进程正式投入运转之后才能看见父进程中已完成初始化部分的状态设定情况同样适用于结束阶段处理完毕信号传递过程等等。 --- ### 示例代码展示 下面给出一段简单示例演示上述理论知识点的应用: ```java public class VisibilityExample { private static boolean ready; private static int number; public static void main(String[] args) throws InterruptedException { new Thread(() -> { while (!ready); // spin-wait until flag is set to true by another thread. System.out.println("Number: " + number); }).start(); Thread.sleep(100); number = 42; // Write operation on shared variable 'number'. ready = true; // Set the readiness indicator which should be visible across threads. /* Without proper synchronization or using a memory barrier such as declaring 'ready' as volatile, there's no guarantee that changes made here will become immediately apparent elsewhere */ } } ``` 在此例子当中如果没有采用适当措施的话很可能造成输出结果不符合预期因为第二个线程未必能及时察觉第一个线程已经改变了相应数值状态。 --- ### 总结 综上所述,深入理解 JMM 对开发人员来说非常重要,尤其是在构建高性能并发应用程序的时候。掌握好其背后的运作机理有助于我们更好地规避潜在风险点进而提高整体软件质量水平。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小倪长头发啦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值