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

企业网站设计需要了解关键少数

企业网站设计需要了解,关键少数,wordpress更换网站,深圳网站建设公司设计0.引言 理解 JMM (Java Memory Model - JMM) 是掌握 Java 并发编程的关键,它定义了多线程环境下,线程如何与主内存以及彼此之间交互内存数据。 核心目标: JMM 旨在解决多线程编程中的三个核心问题: 原子性 (Atomicity)&#xf…

0.引言

理解 JMM (Java Memory Model - JMM) 是掌握 Java 并发编程的关键,它定义了多线程环境下,线程如何与主内存以及彼此之间交互内存数据。

核心目标: JMM 旨在解决多线程编程中的三个核心问题:

  1. 原子性 (Atomicity): 一个操作是不可中断的,要么全部执行成功,要么完全不执行。
  2. 可见性 (Visibility): 一个线程修改了共享变量的值,其他线程能够立即得知这个修改。
  3. 有序性 (Ordering): 程序执行的顺序可能和代码编写的顺序不一致(指令重排序),JMM 定义了在何种情况下这种重排序是被允许或禁止的。

1.第一层:浅显易懂 - 为什么需要 JMM? (The Problem)

想象一下一个简单的场景:

public class VisibilityProblem {private static boolean flag = false; // 共享变量private static int value = 0;        // 共享变量public static void main(String[] args) {// 线程 Anew Thread(() -> {while (!flag) { // 循环检查 flag// 空循环,等待 flag 变为 true}System.out.println("Value: " + value); // 打印 value}).start();// 线程 Bnew Thread(() -> {value = 42;   // 步骤 1:设置 valueflag = true;  // 步骤 2:设置 flag}).start();}
}
  • 直觉期望: 线程 B 先设置 value = 42,然后设置 flag = true。线程 A 看到 flag 变为 true 后跳出循环,打印出 Value: 42
  • 现实问题:
    • 可见性问题: 线程 B 修改了 flagvalue,但这些修改可能只存在于线程 B 的 CPU 缓存或寄存器中,没有立即写回主内存。线程 A 在自己的缓存中看到的 flag 可能仍然是 false,导致它永远无法跳出循环。即使跳出了循环,它看到的 value 也可能是 0 而不是 42
    • 有序性问题: 编译器或处理器为了优化性能,可能会对指令进行重排序。线程 B 中的 flag = true 操作可能在 value = 42 之前执行。如果此时线程 A 看到了 flagtrue 而跳出循环,它看到的 value 就可能是未初始化的 0

JMM 就是为了解决这类在多线程环境下因缓存、指令重排序等优化带来的不可预测行为而制定的规则。


2.第二层:核心概念 - JMM 的抽象模型 (The Abstraction)

JMM 定义了一个抽象的内存模型,它屏蔽了底层硬件的具体实现细节(如 CPU 缓存、缓存一致性协议),为 Java 程序员提供了一个统一的视图:

  1. 主内存 (Main Memory):
    • 存储所有共享变量(实例字段、静态字段、数组元素)的原始值。
    • 是线程间共享的区域。
  2. 工作内存 (Working Memory / Local Memory):
    • 每个线程都有自己的工作内存。
    • 存储该线程使用到的共享变量的副本
    • 线程对共享变量的所有操作(读取、赋值等)都必须在工作内存中进行,不能直接读写主内存
    • 工作内存是 JMM 的一个抽象概念,它涵盖了 CPU 寄存器、各级缓存、写缓冲区等硬件优化。

内存交互操作 (8 种原子操作)

JMM 定义了 8 种原子操作来描述线程、工作内存和主内存之间的交互:

  • lock (锁定):作用于主内存变量,标识其为线程独占状态。
  • unlock (解锁):作用于主内存变量,释放锁定状态。
  • read (读取):作用于主内存变量,将变量值从主内存传输到线程的工作内存。
  • load (载入):作用于工作内存变量,将 read 操作得到的值放入工作内存的变量副本中。
  • use (使用):作用于工作内存变量,将变量值传递给执行引擎(如进行计算)。
  • assign (赋值):作用于工作内存变量,将执行引擎接收到的值赋给工作内存变量。
  • store (存储):作用于工作内存变量,将变量值从工作内存传输到主内存。
  • write (写入):作用于主内存变量,将 store 操作传输过来的值放入主内存变量中。

规则: JMM 规定这些操作必须满足特定的顺序和约束(如 readloadstorewrite 必须成对按顺序出现),但允许在成对操作之间插入其他操作(这是导致可见性和有序性问题的根源之一)。更关键的规则体现在 happens-before 原则上。


3.第三层:关键机制 - Happens-Before (HB)

happens-before 是 JMM 的核心概念,它定义了两个操作之间的偏序关系。如果操作 A happens-before 操作 B,那么:

  1. 可见性保证: A 对共享变量的修改(结果)一定对 B 可见。
  2. 有序性保证: A 在程序顺序上一定排在 B 之前执行(禁止某些重排序)。

注意: happens-before 并不一定意味着时间上的先后!它强调的是可见性和顺序的保证。如果两个操作之间没有 happens-before 关系,JVM 可以随意对它们进行重排序。

3.1JMM 定义的天然 Happens-Before 规则

  1. 程序次序规则 (Program Order Rule):同一个线程中,按照控制流顺序(可能是分支、循环等),前面的操作 happens-before 后面的操作。
  2. 管程锁定规则 (Monitor Lock Rule): 一个 unlock 操作 happens-before 于后续对同一个锁lock 操作。
  3. volatile 变量规则 (Volatile Variable Rule): 对一个 volatile 变量的写操作 happens-before 于后续对这个变量的读操作。
  4. 线程启动规则 (Thread Start Rule): Thread.start() 调用 happens-before 于被启动线程中的任何操作。
  5. 线程终止规则 (Thread Termination Rule): 线程中的所有操作都 happens-before 于其他线程检测到该线程已经终止(如 Thread.join() 返回成功或 Thread.isAlive() 返回 false)。
  6. 线程中断规则 (Thread Interruption Rule): 对线程 interrupt() 的调用 happens-before 于被中断线程检测到中断事件(抛出 InterruptedException 或调用 isInterrupted()/interrupted())。
  7. 对象终结规则 (Finalizer Rule): 一个对象的初始化完成(构造函数执行结束)happens-before 于它的 finalize() 方法的开始。
  8. 传递性 (Transitivity): 如果 A happens-before B,且 B happens-before C,那么 A happens-before C。

happens-before 的意义: 程序员只需要利用这些规则(主要是通过 synchronizedvolatilefinal 等关键字以及 java.util.concurrent 包中的工具),就能确保多线程操作的可见性和有序性,无需关心底层复杂的缓存和重排序细节。


4.第四层:深入剖析 - volatile 关键字

volatile 是 JMM 中最重要的关键字之一,它提供了比 synchronized 更轻量级的同步机制。

4.1volatile 的语义

  1. 保证可见性:
    • 对一个 volatile 变量的写操作,会立即刷新到主内存
    • 对一个 volatile 变量的读操作,会从主内存中读取最新的值(或保证看到最近写入的值)。
    • 这通过禁止编译器/处理器对 volatile 变量的读写操作进行缓存优化来实现。
  2. 禁止指令重排序 (部分):
    • 编译器在生成字节码时,会在 volatile 写操作前后插入写屏障 (StoreStore + StoreLoad)
    • volatile 读操作前后插入读屏障 (LoadLoad + LoadStore)
    • 写屏障 (Store Barrier):
      • StoreStore: 确保在该屏障之前的所有普通写操作都刷新到主内存(对其他线程可见)。
      • StoreLoad: 确保在该屏障之前volatile 写操作都完成,并且刷新到主内存后,才能执行该屏障之后volatile 读/写操作(开销较大,通常由 StoreLoad 承担)。
    • 读屏障 (Load Barrier):
      • LoadLoad: 确保在该屏障之后的所有操作(普通读或 volatile 读)都在该屏障之后的读操作之前执行(禁止重排序),并且强制从主内存或最新缓存中加载数据。
      • LoadStore: 确保在该屏障之后的所有操作都在该屏障之后的写操作之前执行(禁止重排序),并且这些写操作的数据依赖在该屏障之前的读操作已完成。
    • 这些屏障共同作用,确保了 volatile 变量读写操作相对于其前后代码的相对顺序,从而实现了 happens-before 规则中的有序性保证。

4.2volatile 的局限性

  • 不保证原子性: volatile 不能保证复合操作的原子性。例如 volatile int count; count++; 这个操作 (count++ 包含读-改-写三步) 在多线程下仍然是不安全的。需要使用 synchronizedAtomicInteger 等。

4.3volatile 的典型用法

  • 状态标志位: 如开头的例子,用 volatile boolean flag; 来安全地通知其他线程状态改变。
  • 一次性安全发布 (One-Time Safe Publication): 利用 volatile 写操作的 StoreStore 屏障,确保在发布对象引用之前,对象的初始化已经完全完成(构造函数结束)。
    public class Singleton {private static volatile Singleton instance; // volatile 保证安全发布private Singleton() {}public static Singleton getInstance() {if (instance == null) { // 第一次检查 (无锁)synchronized (Singleton.class) {if (instance == null) { // 第二次检查 (加锁)instance = new Singleton(); // volatile 写}}}return instance;}
    }
    
    如果没有 volatile,其他线程可能看到一个未初始化完成Singleton 对象(指令重排序导致引用赋值在构造函数完成之前发生)。
  • 独立观察 (Independent Observation): 定期发布观察结果供其他程序使用。
  • 开销较低的读-写锁策略: 当读远多于写时,可以用 volatile 保证写操作的可见性,读操作不需要加锁(但写操作需要额外的同步机制如 synchronized 或 CAS 来保证原子性)。

5.第五层:JMM 与并发编程实践

理解 JMM 对于编写正确、高效、可预测的并发程序至关重要:

  1. 优先使用高级并发工具: java.util.concurrent 包 (ConcurrentHashMap, ExecutorService, CountDownLatch, CyclicBarrier, AtomicXxx 类等) 是构建在 JMM 基础之上的,它们通常比直接使用 synchronizedvolatile 更安全、更高效、更易用。理解 JMM 能让你更好地理解和使用这些工具。
  2. 正确使用 synchronized: synchronized 块不仅提供互斥(原子性),也提供强大的内存语义:进入 synchronized 块(获得锁)相当于执行一个 volatile 操作(能看到之前持有该锁的线程的所有修改),退出 synchronized 块(释放锁)相当于执行一个 volatile 操作(将修改刷新到主内存)。这确保了临界区内外操作的可见性和有序性。
  3. 理解 final 字段:final 修饰的字段在构造函数中初始化后,其值对其他线程是可见的(无需同步),前提是对象的引用本身是正确发布的(例如通过 volatilesynchronized 安全发布规则)。这是 JMM 对 final 的特殊保证。
  4. 避免过度同步: 不必要的同步会带来性能开销。理解 JMM 可以帮助你判断何时需要同步(主要是为了保护共享可变状态),何时可以避免。
  5. 警惕内存可见性导致的微妙 Bug: 很多并发 Bug 不是由竞态条件引起的,而是由内存可见性问题引起的。理解 JMM 是诊断这类 Bug 的基础。

6.总结

  • JMM 是什么? 一个规范,定义了多线程环境下 Java 程序如何与内存交互,确保程序在并发执行时的可见性有序性(以及通过其他机制如锁保证的原子性)。
  • 核心问题: 解决因 CPU 缓存、指令重排序导致的可见性有序性问题。
  • 抽象模型: 主内存(共享)和工作内存(线程私有副本),定义了 8 种内存交互操作。
  • 核心机制: happens-before 关系。它定义了操作间的可见性和顺序保证。JMM 定义了一系列天然规则(程序次序、锁、volatile、线程启动/终止等)。
  • volatile 关键字:
    • 保证可见性(写立即刷新,读获取最新值)。
    • 通过内存屏障StoreStore, StoreLoad, LoadLoad, LoadStore禁止特定类型的指令重排序
    • 不保证原子性
  • 实践意义: 理解 JMM 是编写正确、高效并发 Java 程序的基础。它解释了高级并发工具的工作原理,指导你正确使用 synchronizedvolatilefinal,并帮助你诊断复杂的并发 Bug。

掌握 JMM 需要时间和实践。从理解基本问题和抽象模型开始,逐步深入到 happens-beforevolatile 的细节,最终将其应用于并发编程实践中,是学习 JMM 的有效路径。

http://www.dtcms.com/wzjs/12315.html

相关文章:

  • 类似于众人帮的做任务赚佣金网站怎么做网页设计的页面
  • 佛山响应式网站开发上海网上推广
  • 最专业的做网站公司新浪疫情实时数据
  • 句容建设质检站网站青岛seo排名公司
  • 大兴智能网站建设哪家好中国百强城市榜单
  • qq小程序开发seo网络推广报价
  • 做苗木的哪个网站效果好湖南正规seo公司
  • 广州专业网站建设网页设计服务seovip培训
  • 免费做的英文网站在线刷关键词网站排名
  • 博客 软件 wordpress武汉seo引擎优化
  • 仁怀网站建设无人区在线观看高清1080
  • 个人简单网站页千锋教育学费
  • 北京网站开发建设seo是什么东西
  • 南阳手机网站建设seo招聘信息
  • 公司淘宝网站怎么建设的更加好深圳网络推广市场
  • 企业做网站需要在通管局备案成都网络营销策划
  • 宜宾市建设工程质量监督站网站seo网站推广优化就找微源优化
  • 北京做网站推广百度搜索排名机制
  • 自己想做网站seo网站怎么搭建
  • wordpress 分类信息主题seo搜索引擎是什么意思
  • 深圳网站建设公司报价网络营销
  • 网站建设季度考核评价工作搜索风云榜入口
  • 昵图网素材图库大图免费360优化大师官方下载最新版
  • php做简单网站教程视频网站优化团队
  • 吴江城乡建设局网站企业网站模板 免费
  • 济南网站制作哪家强seo推广公司招商
  • iis 网站制作企业产品网络推广
  • 海南网址wordpress seo教程
  • 幼教资源网网站开发策划书qq引流推广平台
  • 网站开发网页制作薪资软文是什么东西