Java-内存模型(JSR-133)

Java 内存模型(Java Memory Model,JMM)看上去和 Java 内存结构(JVM 运行时内存结构)差不多,但这两者并不是一回事。JMM 并不像 JVM 内存结构一样是真实存在的,它只是一个抽象的概念。

Java 的线程间通过共享内存(Java堆和方法区)进行通信,在通信过程中会存在一系列如可见性、原子性、顺序性等问题,而 JMM 就是围绕着多线程通信以及与其相关的一系列特性而建立的模型。

现在说 JMM 一般指的是 JDK 5 开始使用的新的内存模型,主要由 JSR-133: JavaTM Memory Model and Thread Specificationhttp://www.cs.umd.edu/users/pugh/java/memoryModel/http://ifeve.com/jsr133-cn/)描述。

一、多线程的特性

原子性

一个操作不可被中断,要么执行完成,要么就不执行。

public static int k = 0;
// 多线程去操作一个共享变量,多运行几次,可以看到不一样的结果
public static void main(String[] args) throws InterruptedException {
    ThreadPoolExecutor tpe = new ThreadPoolExecutor(10, 10, 0, TimeUnit.MICROSECONDS, new LinkedBlockingQueue<>(1000));
    for (int i = 0; i < 1000; i++) {
        tpe.execute(() -> {
            k++;
        });
    }
    Thread.sleep(1000);
    System.out.println(k);
    tpe.shutdown();
}

可见性

多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看到修改的值。

private static boolean flag = false;
// 多运行几次,会出现无法结束的情况
public static void main(String[] args) throws InterruptedException {
    new Thread(() -> {
        while (true) {
            if (flag) {
                System.out.println("------------------");
                break;
            }
        }
    }).start();

    // Thread.sleep(100);

    new Thread(() -> {
        flag = true;
    }).start();
}

有序性

程序执行的顺序按照代码的先后顺序执行。

编译器和处理器会对指令进行重排序来达到优化效果,重排序后不会影响单线程执行的结果,但可能会影响多线程并发执行的结果。

二、Java 内存模型

为了保证共享内存的正确性(可见性、有序性、原子性),JMM 定义了共享内存系统中多线程程序读写操作行为的规范。

JMM 解决并发问题主要采用两种方式:限制处理器优化和使用内存屏障。

主内存与工作内存

Java 内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,用于存储主存中要使用的变量的副本。

线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。

不同的线程之间无法直接访问对方工作内存中的变量。

线程间变量的传递需要自己的工作内存和主存之间进行数据同步。

主内存与工作内存间交互操作(了解)

Java 内存模型定义了八种操作来完成

lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。

happens-before

在JMM中,如果一个操作执行的结果需要对另一个操作可见(并不意味着前一个操作必须要在后一个操作之前执行),那么这两个操作之间必须要存在 happens-before 关系。

三、Java 内存模型的实现

Java 内存模型,除了定义了一套规范,还提供了一系列语义,封装了底层实现后,供开发者直接使用。

如 volatile、synchronized、final、concurren 包等。这些就是 Java 内存模型封装了底层的实现后提供给程序员使用的一些关键字。

原子性

给关键代码加锁

public static int k = 0;

public static void main(String[] args) throws InterruptedException {
    ThreadPoolExecutor tpe = new ThreadPoolExecutor(10, 10, 0, TimeUnit.MICROSECONDS, new LinkedBlockingQueue<>(1000));
    for (int i = 0; i < 1000; i++) {
        tpe.execute(() -> {
            synchronized (tpe) {
                k++;
            }
        });
    }
    Thread.sleep(1000);
    System.out.println(k);
    tpe.shutdown();
}

用 concurren 包中的原子变量代替基本变量

public static AtomicInteger k = new AtomicInteger(0);

public static void main(String[] args) throws InterruptedException {
    ThreadPoolExecutor tpe = new ThreadPoolExecutor(10, 10, 0, TimeUnit.MICROSECONDS, new LinkedBlockingQueue<>(1000));
    for (int i = 0; i < 1000; i++) {
        tpe.execute(() -> {
            k.incrementAndGet();
        });
    }
    Thread.sleep(1000);
    System.out.println(k);
    tpe.shutdown();
}

可见性

给变量加上 volatile 修饰

private static volatile boolean flag = false;

public static void main(String[] args) throws InterruptedException {
    new Thread(() -> {
        while (true) {
            if (flag) {
                System.out.println("------------------");
                break;
            }
        }
    }).start();

    Thread.sleep(100);

    new Thread(() -> {
        flag = true;
    }).start();
}

有序性

使用 synchronized 和 volatile 来保证多线程之间操作的有序性。

volatile 关键字会禁止指令重排。synchronized 关键字保证同一时刻只允许一条线程操作。



https://www.hollischuang.com/archives/2509

https://www.hollischuang.com/archives/2550

http://www.54tianzhisheng.cn/2018/02/28/Java-Memory-Model/

原文地址:https://www.cnblogs.com/jhxxb/p/10940149.html

时间: 2024-10-12 07:19:40

Java-内存模型(JSR-133)的相关文章

深入理解Java内存模型(1 ) -- 基础(转载)

原文地址:http://www.infoq.com/cn/articles/java-memory-model-1 并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体).通信是指线程之间以何种机制来交换信息.在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递. 在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信.在消息传递的并发模型里,线程之间没有公共状态,线

深入理解Java内存模型-volatile

volatile的特性 当我们声明共享变量为volatile后,对这个变量的读/写将会很特别.理解volatile特性的一个好方法是:把对volatile变量的单个读/写,看成是使用同一个监视器锁对这些单个读/写操作做了同步.下面我们通过具体的示例来说明,请看下面的示例代码: class VolatileFeaturesExample { volatile long vl = 0L; //使用volatile声明64位的long型变量 public void set(long l) { vl =

深入理解Java内存模型之系列篇[转]

原文链接:http://blog.csdn.net/ccit0519/article/details/11241403 深入理解Java内存模型(一)——基础 并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体).通信是指线程之间以何种机制来交换信息.在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递. see:命令式编程.函数式编程 在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存

修复 Java 内存模型,第 1 部分——Brian Goetz

转自Java并发大师Brain Goetz:http://www.ibm.com/developerworks/cn/java/j-jtp02244/ (中文地址) http://www.ibm.com/developerworks/java/library/j-jtp02244/index.html (英文地址) 什么是 Java 内存模型,最初它是怎样被破坏的? 简介: 活跃了将近三年的 JSR 133,近期发布了关于如何修复 Java 内存模型(Java Memory Model, JMM

深入理解Java内存模型(一)——基础

并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体).通信是指线程之间以何种机制来交换信息.在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递. 在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信.在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的发送消息来显式进行通信. 同步是指程序用于控制不同线程之间操作发生相对顺序的机制.在共享内存并发

深入理解Java内存模型(三)——顺序一致性

本文属于作者原创,原文发表于InfoQ:http://www.infoq.com/cn/articles/java-memory-model-3 数据竞争与顺序一致性保证 当程序未正确同步时,就会存在数据竞争.java内存模型规范对数据竞争的定义如下: 在一个线程中写一个变量, 在另一个线程读同一个变量, 而且写和读没有通过同步来排序. 当代码中包含数据竞争时,程序的执行往往产生违反直觉的结果(前一章的示例正是如此).如果一个多线程程序能正确同步,这个程序将是一个没有数据竞争的程序. JMM对正

修复 Java 内存模型,第 2 部分——Brian Goetz

转自Java并发大师Brain Goetz:http://www.ibm.com/developerworks/cn/java/j-jtp03304/ (中文地址) http://www.ibm.com/developerworks/java/library/j-jtp03304/index.html (英文地址) 在 JSR 133 中 JMM 会有什么改变? 活跃了将近三年的 JSR 133,近期发布了关于如何修复 Java 内存模型(Java Memory Model, JMM)的公开建议

java内存模型二

并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体).通信是指线程之间以何种机制来交换信息.在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递. 在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信.在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的发送消息来显式进行通信. 同步是指程序用于控制不同线程之间操作发生相对顺序的机制.在共享内存并发

Java内存模型-jsr133规范介绍(转)

最近在看<深入理解Java虚拟机:JVM高级特性与最佳实践>讲到了线程相关的细节知识,里面讲述了关于java内存模型,也就是jsr 133定义的规范. 系统的看了jsr 133规范的前面几个章节的内容,觉得受益匪浅.废话不说,简要的介绍一下java内存规范. 什么是内存规范 在jsr-133中是这么定义的 A memory model describes, given a program and an execution trace of that program, whether the e

深入理解JMM(Java内存模型) --(四)volatile

volatile的特性 当我们声明共享变量为volatile后,对这个变量的读/写将会很特别.理解volatile特性的一个好方法是:把对volatile变量的单个读/写,看成是使用同一个监视器锁对这些单个读/写操作做了同步.下面我们通过具体的示例来说明,请看下面的示例代码: [java] view plain copy class VolatileFeaturesExample { volatile long vl = 0L;  //使用volatile声明64位的long型变量 public