【Java并发编程】JMM - Java 内存模型

一、什么是 JMM

JMM,全程是 Java Memory Model ,直译就是 Java 内存模型。根据这个名字,可以知道它是 Java 设计用来管理内存的一个模型。

Java 中的内存分为主内存和本地内存,线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存中存储了该线程以读/写共享变量的副本。下图很清晰地阐述了这个关系 :

【Java并发编程】JMM - Java 内存模型_第1张图片

因为 Java 通过共享内存作为线程间的通信机制,因此 JMM 和线程通信息息相关,可以说 JMM 控制线程的通信,具体来说 JMM 控制一个线程对共享变量的写入何时对另一个线程可见 。



二、 JMM 基础

本节介绍了解 JMM 需要的一些前置知识, 即和 JMM 有关的一些概念

2.1 Java 线程通信机制

线程通信机制一共有两种(共享内存 和 消息传递), Java 使用 共享内存模型 作为线程同步机制

2.2 同步

同步是指程序如何控制不同线程操作发生的相对顺序,在多线程并发情况下,同步是保证多线程下安全问题的非常重要的操作。

2.3 堆内存

Java 内存有栈内存、堆内存 和 方法区 。 堆内存中含有线程共享的 实例、静态变量 和 数组元素等 ,也就是说堆内存是线程共享区 。

2.4 指令重排

正常情况,按照我们的理解,我们写一段程序在处理器中就是按照我们写的顺序执行的 。 但是实际上,在我们看不到的地方,处理器为了提高执行效率,优化性能,会对指令进行重排 。

以下面的程序为例子,解释指令重排:

int a = 1;  // ....  语句1
int b = 2; // ..... 语句 2

正常情况下, 语句 1 优先于 语句 2 执行 。 但是,处理器实际在执行时,会根据情况进行处理,可能会出现 语句2 优先与 语句1 执行的情况。但是因为对程序的执行结果没有影响, 所以,我们程序员不知道发生了指令重排 。

在单线程情况下,指令重排不影响程序运行的结果。但是多线程情况下,可能会造成影响。因此, JMM 通过插入 内存屏障 禁止特定类型的处理器重排序来保证多线程并发下的程序执行安全 。

2.5 顺序一致性模型

顺序一致性模型,是 JMM 为保证多线程下的安全而提出的,正确同步的程序应该具有顺序一致性,其具有如下特点:

  • 按照程序顺序执行,不能重排序
  • 操作原子执行,对所有线程可见

2.6 同步程序

同步程序并不是完全按照顺序一致性执行,其在临界区内可以进行重排序,这样的设计给了处理器足够的优化空间,同时也没有改变程序的执行结果 。

三、volatile 关键字

3.1 volatile 做什么

volatile 相当于同一个锁对对该变量的读写操作进行了同步,用 volatile 修饰的变量总是能被同步到共享内存中,避免了因为本地内存的存在导致的线程不安全问题 。volatile 具有如下两个特性:

  • 可见性:一个线程的读,总是能看到另一个线程的写
  • 原子性: 对单个变量的读写具有原子性, 但是对复合操作不具有原子性

3.2 volatile 原理

3.2.1 规则

  • 当第二个操作是volatile写时,不管第一个操作是什么,都不能重排序
  • 当第一个操作是volatile读时,不管第二个操作是什么,都不能重排序
  • 当第一个操作是volatile写,第二个操作是volatile读时,不能重排序

3.2.2 实现

生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序

四、锁的内存语义

  • 锁释放与volatile写有相同的内存语义
  • 锁获取与volatile读有相同的内存语义

4.1 AQS Java同步器框架

AQS使用一个整型的volatile变量来维护同步状态, 实现 ReentrantLock

4.2 concurrent包

concurrent 包依赖于 CAS 和 volatile 实现

  • 声明共享变量为volatile
  • 使用CAS的原子条件更新来实现线程之间的同步
  • 配合以 volatile 的读/写和 CAS 所具有的 volatile 读和写的内存语义来实现线程之间的通信

你可能感兴趣的:(Java,java,jvm,开发语言)