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"的情况。

相关文章
|
4月前
|
缓存 Java 编译器
JUC 并发编程之JMM
Java内存模型是Java虚拟机(JVM)规范中定义的一组规则,用于屏蔽各种硬件和操作系统的内存访问差异,保证多线程情况下程序的正确执行。Java内存模型规定了线程之间如何交互以及线程和内存之间的关系。它主要解决的问题是可见性、原子性和有序性。
|
7月前
|
缓存 安全 Java
JUC并发编程-共享模型无锁
CAS 与 volatile CAS概述 CAS的全称是: Compare And Swap(比较再交换),它体现的一种乐观锁的思想,在无锁情况下保证线程操作共享数据的原子性。 在JUC( java.util.concurrent )包下实现的很多类都用到了CAS操作 AbstractQueuedSynchronizer(AQS框架) AtomicXXX类 例子: 我们基于JMM内存模型进行说明 线程1与线程2都从主内存中获取变量int a = 100,同时放到各个线程的工作内存中 一个当前内存值V、旧的预期值A、即将更新的值B,当且仅当旧的预期值A和内存值V相同时,将内存值修
25 0
|
7天前
|
Java 程序员 开发者
深入理解Java并发编程:线程同步与锁机制
【4月更文挑战第30天】 在多线程的世界中,确保数据的一致性和线程间的有效通信是至关重要的。本文将深入探讨Java并发编程中的核心概念——线程同步与锁机制。我们将从基本的synchronized关键字开始,逐步过渡到更复杂的ReentrantLock类,并探讨它们如何帮助我们在多线程环境中保持数据完整性和避免常见的并发问题。文章还将通过示例代码,展示这些同步工具在实际开发中的应用,帮助读者构建对Java并发编程深层次的理解。
|
2月前
|
缓存 安全 Java
深入理解Java并发编程:线程安全与锁优化
【2月更文挑战第29天】在Java中,多线程编程是提高应用程序性能和响应能力的关键。然而,随之而来的并发问题,如数据不一致、死锁等,也给开发者带来了挑战。本文将深入探讨Java并发编程中的线程安全问题,并介绍如何通过锁优化来提高程序性能。我们将从基本概念开始,逐步深入到高级技巧,帮助读者更好地理解和应对并发编程中的挑战。
|
4月前
|
安全 Java
JUC并发编程之原子类
并发编程是现代计算机应用中不可或缺的一部分,而在并发编程中,处理共享资源的并发访问是一个重要的问题。为了避免多线程访问共享资源时出现竞态条件(Race Condition)等问题,Java提供了一组原子类(Atomic Classes)来支持线程安全的操作。
|
7月前
|
存储 缓存 安全
JUC第二讲:Java并发理论基础:Java内存模型(JMM)与线程
JUC第二讲:Java并发理论基础:Java内存模型(JMM)与线程
|
11月前
|
缓存 安全 Java
【Java并发编程 二】JMM内存模型(三)
【Java并发编程 二】JMM内存模型
85 0
|
11月前
|
存储 缓存 安全
【Java并发编程 二】JMM内存模型(一)
【Java并发编程 二】JMM内存模型(一)
200 0
|
存储 Java 调度
并发编程(二)JMM模型
并发编程(二)JMM模型
103 0
|
Web App开发 存储 安全
Java并发编程之多线程
我们首先,先要了解什么是进程,什么是线程。