juc并发编程02——JMM模型(上)

简介: 我们在这篇文章中将介绍JMM模型,也就是java内存模型。注意,本文所提到的JMM模型与JVM内存模型属于不同层次的内容。JVM内存模型讲的是物理内存空间的分配,而JMM则强调对于JVM内存模型的抽象。

1.java内存模型

在计算机中,为了解决主内存的速度跟不上处理器速度的问题,我们给每个处理器添加一级或多级高速缓存(如下图)。但是,每个处理器上缓存的数据如何保证一致性呢?

为了实现缓存数据的一致性,我们计算机中使用了缓存一致性协议。java中也有类似的机制来实现多线程的数据模型。

java的内存模型规定如下:


所有的变量均存在主内存中(这里及后文的变量指所有可能出现竞争的变量:包括成员变量、静态变量,不包括局部变量)

每个线程都有自己的工作内存,线程对变量的操作必须在工作内存中完成。不同线程之间的工作内存相互隔离。

那么这个内存模型在jvm中究竟是如何实现的呢?


主内存:对应堆中的实例对象

工作内存:对应虚拟机栈,虚拟机可能会对这部分内存进行优化,将其放入寄存器或者高速缓存中。

看如下实例。

public class Demo4 {
private static int i = 0;
    public static void main(String[] args) throws InterruptedException {
        new Thread(()-> {
            for(int j = 0; j < 1000000; j++) {
                i++;
            }
            System.out.println("thread1 run pass");
        }).start();
        new Thread(()-> {
            for(int j = 0; j < 1000000; j++) {
                i++;
            }
            System.out.println("thread2 run pass");
        }).start();
        Thread.sleep(1000);
        System.out.println(i);
    }
}

运行结果如下:

thread1 run pass
thread2 run pass
1729677

为什么呢?如果您有jvm的基础,就应该知道,原来自增操作并不是原子操作。可能出现如下图的情况。后面我们将讲解如何解决这个问题。

2.重排序

在编译或者运行期间,有可能会对指令进行重排序。有以下可能:

  • java编译器根据对java语义的理解进行重排序。
  • 现代处理器可能会对机器指令自主进行重排序。

在单线程的环境下,指令重排序可以在不改变执行结果的前提下优化代码的执行效率。但是多线程下指令重排序可能会导致一些问题。

public class Demo5 {
    private static int a = 0;
    private static int b = 0;
    public static void main(String[] args) {
        new Thread(() -> {
            if(b == 1) {
                if(a == 0) {
                    System.out.println("A");
                }
                if(a == 1) {
                    System.out.println("B");
                }
            }
        }
        ).start();
        new Thread(
                () -> {
                    a = 1;
                    b = 1;
                }
        ).start();
    }

按朴素的理解,上面代码的输出只有可能是"B"或者没有输出。但是上面a,b的赋值可能被重排序

public class Demo5 {
    private static int a = 0;
    private static int b = 0;
    public static void main(String[] args) {
        new Thread(() -> {
            if(b == 1) {
                if(a == 0) {
                    System.out.println("A");
                }
                if(a == 1) {
                    System.out.println("B");
                }
            }
        }
        ).start();
        new Thread(
                () -> {
                    b = 1;
                    a = 1;
                }
        ).start();
    }

因此可能出现结果为"A"的情况。

相关文章
|
6月前
|
缓存 Java 编译器
JUC 并发编程之JMM
Java内存模型是Java虚拟机(JVM)规范中定义的一组规则,用于屏蔽各种硬件和操作系统的内存访问差异,保证多线程情况下程序的正确执行。Java内存模型规定了线程之间如何交互以及线程和内存之间的关系。它主要解决的问题是可见性、原子性和有序性。
|
6月前
|
Java 程序员 开发者
深入理解Java并发编程:线程同步与锁机制
【4月更文挑战第30天】 在多线程的世界中,确保数据的一致性和线程间的有效通信是至关重要的。本文将深入探讨Java并发编程中的核心概念——线程同步与锁机制。我们将从基本的synchronized关键字开始,逐步过渡到更复杂的ReentrantLock类,并探讨它们如何帮助我们在多线程环境中保持数据完整性和避免常见的并发问题。文章还将通过示例代码,展示这些同步工具在实际开发中的应用,帮助读者构建对Java并发编程深层次的理解。
|
3月前
|
存储 Java
JUC(8)JMM
这篇文章介绍了Java内存模型(JMM),解释了volatile关键字的作用,包括确保变量的可见性、禁止指令重排但不保证操作的原子性,并探讨了单例模式的实现方式,包括饿汉式和懒汉式单例模式的示例代码。
JUC(8)JMM
|
存储 缓存 安全
JUC第二讲:Java并发理论基础:Java内存模型(JMM)与线程
JUC第二讲:Java并发理论基础:Java内存模型(JMM)与线程
109 0
|
存储 设计模式 SpringCloudAlibaba
JUC并发编程(三):并发编程基石之AQS
**AQS: AbstractQueuedSynchronizer**,顾名思义,翻译过来叫**抽象队列同步器**,它是JUC并发编程的**基石**,定义了一套多线程访问共享资源的同步器框架,众多同步类底层都是基于AQS实现的,如常用的ReentrantLock、Semaphore、CountDownLatch等。
102 0
JUC并发编程(三):并发编程基石之AQS
|
缓存 安全 Java
【Java并发编程 二】JMM内存模型(三)
【Java并发编程 二】JMM内存模型
104 0
|
安全 Java Linux
JUC (java并发编程学习分享篇)
JUC (java并发编程学习分享篇)
74 0
|
存储 Java 调度
并发编程(二)JMM模型
并发编程(二)JMM模型
121 0
|
存储 缓存 Java
JUC并发编程学习(十六)谈谈java内存模型JMM
JUC并发编程学习(十六)谈谈java内存模型JMM
JUC并发编程学习(十六)谈谈java内存模型JMM
JUC并发编程学习(九)-读写锁
JUC并发编程学习(九)-读写锁
JUC并发编程学习(九)-读写锁