多线程与Java的JMM内存模型

Java内存模型(JMM):揭开多线程编程的神秘面纱 在Java的世界里,多线程编程是提升程序性能的利器,但也带来了复杂的内存可见性和线程安全问题。为了理解并解决这些问题,我们需要深入Java内存模型(Java Memory Model, JMM)的底层机制。Java内存模型定义了Java虚拟机(JVM)在计算机内存(RAM)中的工作方式,它规定了线程如何与内存交互,以及如何保证多线程环境下的数据一致性。理解JMM是编写正确、高效并发程序的关键。 阅读详情

多线程与Java的JMM内存模型

共享内存模型指的就是Java内存模型(简称JMM),JMM决定一个线程对共享变量的写入时,能对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。

从上图来看,线程A与线程B之间如要通信的话,必须要经历下面2个步骤:

1. 首先,线程A把本地内存A中更新过的共享变量刷新到主内存中去。

2. 然后,线程B到主内存中去读取线程A之前已更新过的共享变量。

下面通过示意图来说明这两个步骤:

 

如上图所示,本地内存A和B有主内存中共享变量x的副本。假设初始时,这三个内存中的x值都为0。线程A在执行时,把更新后的x值(假设值为1)临时存放在自己的本地内存A中。当线程A和线程B需要通信时,线程A首先会把自己本地内存中修改后的x值刷新到主内存中,此时主内存中的x值变为了1。随后,线程B到主内存中去读取线程A更新后的x值,此时线程B的本地内存的x值也变为了1。

从整体来看,这两个步骤实质上是线程A在向线程B发送消息,而且这个通信过程必须要经过主内存。JMM通过控制主内存与每个线程的本地内存之间的交互,来为java程序员提供内存可见性保证。

总结:什么是Java内存模型:java内存模型简称jmm,定义了一个线程对另一个线程可见。共享变量存放在主内存中,每个线程都有自己的本地内存,当多个线程同时访问一个数据的时候,可能本地内存没有及时刷新到主内存,所以就会发生线程安全问题。

 

 

补充转载


 

在并发编程中,多个线程之间采取什么机制进行通信(信息交换),什么机制进行数据的同步?

  在Java语言中,采用的是共享内存模型来实现多线程之间的信息交换和数据同步的。

  线程之间通过共享程序公共的状态,通过读-写内存中公共状态的方式来进行隐式的通信。同步指的是程序在控制多个线程之间执行程序的相对顺序的机制,在共享内存模型中,同步是显式的,程序员必须显式指定某个方法/代码块需要在多线程之间互斥执行。

  在说Java内存模型之前,我们先说一下Java的内存结构,也就是运行时的数据区域:

  Java虚拟机在执行Java程序的过程中,会把它管理的内存划分为几个不同的数据区域,这些区域都有各自的用途、创建时间、销毁时间。

                                                                                          

Java运行时数据区分为下面几个内存区域:

1.PC寄存器/程序计数器:

  严格来说是一个数据结构,用于保存当前正在执行的程序的内存地址,由于Java是支持多线程执行的,所以程序执行的轨迹不可能一直都是线性执行。当有多个线程交叉执行时,被中断的线程的程序当前执行到哪条内存地址必然要保存下来,以便用于被中断的线程恢复执行时再按照被中断时的指令地址继续执行下去。为了线程切换后能恢复到正确的执行位置,每个线程都需要有一个独立的程序计数器,各个线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存,这在某种程度上有点类似于“ThreadLocal”,是线程安全的。

2.Java栈 Java Stack:

  Java栈总是与线程关联在一起的,每当创建一个线程,JVM就会为该线程创建对应的Java栈,在这个Java栈中又会包含多个栈帧(Stack Frame),这些栈帧是与每个方法关联起来的,每运行一个方法就创建一个栈帧,每个栈帧会含有一些局部变量、操作栈和方法返回值等信息。每当一个方法执行完成时,该栈帧就会弹出栈帧的元素作为这个方法的返回值,并且清除这个栈帧,Java栈的栈顶的栈帧就是当前正在执行的活动栈,也就是当前正在执行的方法,PC寄存器也会指向该地址。只有这个活动的栈帧的本地变量可以被操作栈使用,当在这个栈帧中调用另外一个方法时,与之对应的一个新的栈帧被创建,这个新创建的栈帧被放到Java栈的栈顶,变为当前的活动栈。同样现在只有这个栈的本地变量才能被使用,当这个栈帧中所有指令都完成时,这个栈帧被移除Java栈,刚才的那个栈帧变为活动栈帧,前面栈帧的返回值变为这个栈帧的操作栈的一个操作数。

  由于Java栈是与线程对应起来的,Java栈数据不是线程共有的,所以不需要关心其数据一致性,也不会存在同步锁的问题。

  在Java虚拟机规范中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。在Hot Spot虚拟机中,可以使用-Xss参数来设置栈的大小。栈的大小直接决定了函数调用的可达深度。

                                                   

 

3.堆 Heap:

  堆是JVM所管理的内存中国最大的一块,是被所有Java线程锁共享的,不是线程安全的,在JVM启动时创建。堆是存储Java对象的地方,这一点Java虚拟机规范中描述是:所有的对象实例以及数组都要在堆上分配。Java堆是GC管理的主要区域,从内存回收的角度来看,由于现在GC基本都采用分代收集算法,所以Java堆还可以细分为:新生代和老年代;新生代再细致一点有Eden空间、From Survivor空间、To Survivor空间等。

4.方法区Method Area:

  方法区存放了要加载的类的信息(名称、修饰符等)、类中的静态常量、类中定义为final类型的常量、类中的Field信息、类中的方法信息,当在程序中通过Class对象的getName.isInterface等方法来获取信息时,这些数据都来源于方法区。方法区是被Java线程锁共享的,不像Java堆中其他部分一样会频繁被GC回收,它存储的信息相对比较稳定,在一定条件下会被GC,当方法区要使用的内存超过其允许的大小时,会抛出OutOfMemory的错误信息。方法区也是堆中的一部分,就是我们通常所说的Java堆中的永久区 Permanet Generation,大小可以通过参数来设置,可以通过-XX:PermSize指定初始值,-XX:MaxPermSize指定最大值。

5.常量池Constant Pool:

  常量池本身是方法区中的一个数据结构。常量池中存储了如字符串、final变量值、类名和方法名常量。常量池在编译期间就被确定,并保存在已编译的.class文件中。一般分为两类:字面量和应用量。字面量就是字符串、final变量等。类名和方法名属于引用量。引用量最常见的是在调用方法的时候,根据方法名找到方法的引用,并以此定为到函数体进行函数代码的执行。引用量包含:类和接口的权限定名、字段的名称和描述符,方法的名称和描述符。

6.本地方法栈Native Method Stack:

  本地方法栈和Java栈所发挥的作用非常相似,区别不过是Java栈为JVM执行Java方法服务,而本地方法栈为JVM执行Native方法服务。本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常。

  

主内存和工作内存:

  Java内存模型的主要目标是定义程序中各个变量的访问规则,即在JVM中将变量存储到内存和从内存中取出变量这样的底层细节。此处的变量与Java编程里面的变量有所不同步,它包含了实例字段、静态字段和构成数组对象的元素,但不包含局部变量和方法参数,因为后者是线程私有的,不会共享,当然不存在数据竞争问题(如果局部变量是一个reference引用类型,它引用的对象在Java堆中可被各个线程共享,但是reference引用本身在Java栈的局部变量表中,是线程私有的)。为了获得较高的执行效能,Java内存模型并没有限制执行引起使用处理器的特定寄存器或者缓存来和主内存进行交互,也没有限制即时编译器进行调整代码执行顺序这类优化措施。

  JMM规定了所有的变量都存储在主内存(Main Memory)中。每个线程还有自己的工作内存(Working Memory),线程的工作内存中保存了该线程使用到的变量的主内存的副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量(volatile变量仍然有工作内存的拷贝,但是由于它特殊的操作顺序性规定,所以看起来如同直接在主内存中读写访问一般)。不同的线程之间也无法直接访问对方工作内存中的变量,线程之间值的传递都需要通过主内存来完成。

                                                                   

 

  线程1和线程2要想进行数据的交换一般要经历下面的步骤:

  1.线程1把工作内存1中的更新过的共享变量刷新到主内存中去。

  2.线程2到主内存中去读取线程1刷新过的共享变量,然后copy一份到工作内存2中去。

 

 Java内存模型是围绕着并发编程中原子性、可见性、有序性这三个特征来建立的,那我们依次看一下这三个特征:

  原子性(Atomicity):一个操作不能被打断,要么全部执行完毕,要么不执行。在这点上有点类似于事务操作,要么全部执行成功,要么回退到执行该操作之前的状态。

  基本类型数据的访问大都是原子操作,long 和double类型的变量是64位,但是在32位JVM中,32位的JVM会将64位数据的读写操作分为2次32位的读写操作来进行,这就导致了long、double类型的变量在32位虚拟机中是非原子操作,数据有可能会被破坏,也就意味着多个线程在并发访问的时候是线程非安全的。

下面我们来演示这个32位JVM下,对64位long类型的数据的访问的问题:

public class NotAtomicity {
    //静态变量t
    public  static long t = 0;
    //静态变量t的get方法
    public  static long getT() {
        return t;
    }
    //静态变量t的set方法
    public  static void setT(long t) {
        NotAtomicity.t = t;
    }
    //改变变量t的线程
    public static class ChangeT implements Runnable{
        private long to;
        public ChangeT(long to) {
            this.to = to;
        }
        public void run() {
            //不断的将long变量设值到 t中
            while (true) {
                NotAtomicity.setT(to);
                //将当前线程的执行时间片段让出去,以便由线程调度机制重新决定哪个线程可以执行
                Thread.yield();
            }
        }
    }
    //读取变量t的线程,若读取的值和设置的值不一致,说明变量t的数据被破坏了,即线程不安全
    public static class ReadT implements Runnable{

        public void run() {
            //不断的读取NotAtomicity的t的值
            while (true) {
                long tmp = NotAtomicity.getT();
                //比较是否是自己设值的其中一个
                if (tmp != 100L && tmp != 200L && tmp != -300L && tmp != -400L) {
                    //程序若执行到这里,说明long类型变量t,其数据已经被破坏了
                    System.out.println(tmp);
                }
                ////将当前线程的执行时间片段让出去,以便由线程调度机制重新决定哪个线程可以执行
                Thread.yield();
            }
        }
    }
    public static void main(String[] args) {
        new Thread(new ChangeT(100L)).start();
        new Thread(new ChangeT(200L)).start();
        new Thread(new ChangeT(-300L)).start();
        new Thread(new ChangeT(-400L)).start();
        new Thread(new ReadT()).start();
    }
}

 

我们创建了4个线程来对long类型的变量t进行赋值,赋值分别为100,200,-300,-400,有一个线程负责读取变量t,如果正常的话,读取到的t的值应该是我们赋值中的一个,但是在32的JVM中,事情会出乎预料。如果程序正常的话,我们控制台不会有任何的输出,可实际上,程序一运行,控制台就输出了下面的信息:

-4294967096
4294966896
-4294967096
-4294967096
4294966896
之所以会出现上面的情况,是因为在32位JVM中,64位的long数据的读和写都不是原子操作,即不具有原子性,并发的时候相互干扰了。

  32位的JVM中,要想保证对long、double类型数据的操作的原子性,可以对访问该数据的方法进行同步,就像下面的:

public class Atomicity {
    //静态变量t
    public  static long t = 0;
    //静态变量t的get方法,同步方法
    public synchronized static long getT() {
        return t;
    }
    //静态变量t的set方法,同步方法
    public synchronized static void setT(long t) {
        Atomicity.t = t;
    }
    //改变变量t的线程
    public static class ChangeT implements Runnable{
        private long to;
        public ChangeT(long to) {
            this.to = to;
        }
        public void run() {
            //不断的将long变量设值到 t中
            while (true) {
                Atomicity.setT(to);
                //将当前线程的执行时间片段让出去,以便由线程调度机制重新决定哪个线程可以执行
                Thread.yield();
            }
        }
    }
    //读取变量t的线程,若读取的值和设置的值不一致,说明变量t的数据被破坏了,即线程不安全
    public static class ReadT implements Runnable{

        public void run() {
            //不断的读取NotAtomicity的t的值
            while (true) {
                long tmp = Atomicity.getT();
                //比较是否是自己设值的其中一个
                if (tmp != 100L && tmp != 200L && tmp != -300L && tmp != -400L) {
                    //程序若执行到这里,说明long类型变量t,其数据已经被破坏了
                    System.out.println(tmp);
                }
                ////将当前线程的执行时间片段让出去,以便由线程调度机制重新决定哪个线程可以执行
                Thread.yield();
            }
        }
    }
    public static void main(String[] args) {
        new Thread(new ChangeT(100L)).start();
        new Thread(new ChangeT(200L)).start();
        new Thread(new ChangeT(-300L)).start();
        new Thread(new ChangeT(-400L)).start();
        new Thread(new ReadT()).start();
    }
}

这样做的话,可以保证对64位数据操作的原子性。

 

 可见性:一个线程对共享变量做了修改之后,其他的线程立即能够看到(感知到)该变量这种修改(变化)。

  Java内存模型是通过将在工作内存中的变量修改后的值同步到主内存,在读取变量前从主内存刷新最新值到工作内存中,这种依赖主内存的方式来实现可见性的。

无论是普通变量还是volatile变量都是如此,区别在于:volatile的特殊规则保证了volatile变量值修改后的新值立刻同步到主内存,每次使用volatile变量前立即从主内存中刷新,因此volatile保证了多线程之间的操作变量的可见性,而普通变量则不能保证这一点。

  除了volatile关键字能实现可见性之外,还有synchronized,Lock,final也是可以的。

  使用synchronized关键字,在同步方法/同步块开始时(Monitor Enter),使用共享变量时会从主内存中刷新变量值到工作内存中(即从主内存中读取最新值到线程私有的工作内存中),在同步方法/同步块结束时(Monitor Exit),会将工作内存中的变量值同步到主内存中去(即将线程私有的工作内存中的值写入到主内存进行同步)。

  使用Lock接口的最常用的实现ReentrantLock(重入锁)来实现可见性:当我们在方法的开始位置执行lock.lock()方法,这和synchronized开始位置(Monitor Enter)有相同的语义,即使用共享变量时会从主内存中刷新变量值到工作内存中(即从主内存中读取最新值到线程私有的工作内存中),在方法的最后finally块里执行lock.unlock()方法,和synchronized结束位置(Monitor Exit)有相同的语义,即会将工作内存中的变量值同步到主内存中去(即将线程私有的工作内存中的值写入到主内存进行同步)。

  final关键字的可见性是指:被final修饰的变量,在构造函数数一旦初始化完成,并且在构造函数中并没有把“this”的引用传递出去(“this”引用逃逸是很危险的,其他的线程很可能通过该引用访问到只“初始化一半”的对象),那么其他线程就可以看到final变量的值。

  有序性:对于一个线程的代码而言,我们总是以为代码的执行是从前往后的,依次执行的。这么说不能说完全不对,在单线程程序里,确实会这样执行;但是在多线程并发时,程序的执行就有可能出现乱序。用一句话可以总结为:在本线程内观察,操作都是有序的;如果在一个线程中观察另外一个线程,所有的操作都是无序的。前半句是指“线程内表现为串行语义(WithIn Thread As-if-Serial Semantics)”,后半句是指“指令重排”现象和“工作内存和主内存同步延迟”现象。

Java提供了两个关键字volatile和synchronized来保证多线程之间操作的有序性,volatile关键字本身通过加入内存屏障来禁止指令的重排序,而synchronized关键字通过一个变量在同一时间只允许有一个线程对其进行加锁的规则来实现,

在单线程程序中,不会发生“指令重排”和“工作内存和主内存同步延迟”现象,只在多线程程序中出现。

 

happens-before原则:

  Java内存模型中定义的两项操作之间的次序关系,如果说操作A先行发生于操作B,操作A产生的影响能被操作B观察到,“影响”包含了修改了内存中共享变量的值、发送了消息、调用了方法等。

  下面是Java内存模型下一些”天然的“happens-before关系,这些happens-before关系无须任何同步器协助就已经存在,可以在编码中直接使用。如果两个操作之间的关系不在此列,并且无法从下列规则推导出来的话,它们就没有顺序性保障,虚拟机可以对它们进行随意地重排序。

  a.程序次序规则(Pragram Order Rule):在一个线程内,按照程序代码顺序,书写在前面的操作先行发生于书写在后面的操作。准确地说应该是控制流顺序而不是程序代码顺序,因为要考虑分支、循环结构。

  b.管程锁定规则(Monitor Lock Rule):一个unlock操作先行发生于后面对同一个锁的lock操作。这里必须强调的是同一个锁,而”后面“是指时间上的先后顺序。

  c.volatile变量规则(Volatile Variable Rule):对一个volatile变量的写操作先行发生于后面对这个变量的读取操作,这里的”后面“同样指时间上的先后顺序。

  d.线程启动规则(Thread Start Rule):Thread对象的start()方法先行发生于此线程的每一个动作。

  e.线程终于规则(Thread Termination Rule):线程中的所有操作都先行发生于对此线程的终止检测,我们可以通过Thread.join()方法结束,Thread.isAlive()的返回值等作段检测到线程已经终止执行。

  f.线程中断规则(Thread Interruption Rule):对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测是否有中断发生。

  g.对象终结规则(Finalizer Rule):一个对象初始化完成(构造方法执行完成)先行发生于它的finalize()方法的开始。

  g.传递性(Transitivity):如果操作A先行发生于操作B,操作B先行发生于操作C,那就可以得出操作A先行发生于操作C的结论。

  一个操作”时间上的先发生“不代表这个操作会是”先行发生“,那如果一个操作”先行发生“是否就能推导出这个操作必定是”时间上的先发生 “呢?也是不成立的,一个典型的例子就是指令重排序。所以时间上的先后顺序与happens-before原则之间基本没有什么关系,所以衡量并发安全问题一切必须以happens-before 原则为准。

 

 

JMM内存模型 & 多线程三大特性   提起并发编程,就绕不开并发的三大特性(1.原子性、2.可见性、3.有序性);要解决并发编程中遇到的这三类问题,可能很多人也知道大名鼎鼎的 JMM 内存模型,但又有多少人知道 JMM 内存模型是如何解决这三大问题的呢?本文就从原理层面来细细讲解。   在《并发编程》板块,已经有介绍到 1.计算机原理结构   2.MESI缓存一致性协议。这两篇内容介绍的也只是在硬件CPU层面上的解决方案。在 Java 软件层面,就是我们接下来要介绍的 JMM 内存模型,JMM 内存模型就是基于 CPU 底层架构的一种抽象 阅读详情

相关推荐

电荷灵敏前置放大器噪声优化策略:从理论到实践

本文深入探讨了电荷灵敏前置放大器的噪声优化策略,从理论分析到实践应用。文章系统解析了并联电流噪声与串联电压噪声的来源,并引入噪声等效原理与“冷电容”模型作为统一分析工具。通过详解a、b、c三类噪声的频谱特性,为实战优化提供了清晰指导。核心策略包括选用高跨导、低漏电流FET,优化静态工作点,采用共源-共基结构,精心设计反馈网络,以及实施严格的PCB布局、屏蔽与接地,旨在全面提升微弱信号检测系统的信噪比。

weixin_29292431的博客 467

JMM(Java内存模型)

CPU架构 CPU架构缓存一致性 缓存一致性协议给缓存行(通常为64字节)定义了个状态,用来描述该缓存行是否被多处理器共享、是否修改,缓存一致性协议最出名的是MESI协议。 MESI中每个缓存行都有四个状态,分别是独占E(exclusive)、修改M(modified)、共享S(shared)、失效I(invalid)。 独占(exclusive):仅当前处理器拥有该缓存行,并且没有修改过,是最新的值。 共享(share):有多个处理器拥有该缓存行,每个处理器都没有修改过缓存,是最新的值。 修改(mod

这波不亏的博客 1070

一文带你弄懂thermal governor IPA

当current temp < target temp时,使用k_pu,当current temp >= target temp时,使用k_po。如果current_temperature = switch_on_temp时,则k_p = k_pu,这时Pmax。如果current_temperature = target_temp时,则k_p = k_po,这时Pmax。2、解析dts,获得switch_on_temp和target_temp。3、初始化k_pu/k_po/k_i/k_d的常数。

yxw0609131056的博客 1245

JAVA多线程之JMM

Java内存模型,JMM介绍,包括其三大特性,指令重排以及happens-before关系的介绍

ifhuke的博客 1150

一篇教你杰理63系列蓝牙双设备主从连接传输数据过程及配置

首先打开632sdk的app_config.h文件,将宏CONFIG_APP_SPP_LE写0,将宏CONFIG_APP_MULTI写1,对应的CONFIG_BT_GATT_CLIENT_NUM写1代表我只连接一个主机设备。属性都可以修改为自己想要的属性,然后关掉文件,双击gatt_inc_generator.exe文件运行,就会自动弹出生成/更新后的gatt_profile_cfg.h文件,然后里面就是我们代码里面所需要的配置,都是十六进制表示,找到我们刚刚添加的服务属性配置,复制到代码里面去。

xiaolizaixian的博客 1377

从SPI协议到文件系统:手把手教你用HAL库给W25Q16移植FATFS(附扇区管理优化方案)

本文详细介绍了如何在STM32平台上,通过HAL库的SPI接口为W25Q16 Flash芯片移植FATFS文件系统。文章从SPI通信基础讲起,深入解析了FATFS底层驱动(diskio.c)的移植要点,并重点探讨了针对W25Q16的4KB物理扇区特性进行扇区管理优化的两种方案,旨在帮助开发者构建稳定高效的文件存储方案。

happy2的博客 1043

多线程--JMM(Java Memory Modle,java内存模型)

特性描述解决方法可见性一个线程对共享变量的修改,其他线程能够立即看到。volatile、锁机制有序性程序的执行顺序按照代码的先后顺序执行。volatile、锁机制原子性一个操作是不可分割的,要么全部执行成功,要么全部不执行。原子类、锁机制JMM 是 Java 多线程编程的基础,理解其内存结构和规则,可以帮助我们编写高效、可靠的多线程程序。

zx733988的博客 1063

手把手拆解固态电池成本:2030年真能比液态电池便宜30%吗?

本文深度解析固态电池成本结构,探讨2030年实现比液态电池便宜30%的可行性路径。通过拆解正极材料、负极材料和固态电解质的成本演变,分析关键技术对降本的影响,并预测产业链协同效应下的成本优化趋势。文章指出,在材料创新、设备国产化和政策支持等多重因素推动下,固态电池有望在2030年实现显著降本目标。

kite3的博客 403

清晰万字解析:Java内存模型(JMM)

//Java内存模型JMM用来描述多线程环境中共享变量的内存可见性。//JMM和 Java 运行时内存区域的划分,这两者既有差别又有联系。//JMM内存可见性问题(为什么出现?在哪里出现?怎么解决?)。//JMM 与重排序(保证运行高效)。//JMM与顺序一致性模型(保证答案正确)。

JIGUANGDI 1425

19 04-读取DTC快照信息

为了方便找到故障的原因,车厂一般会在诊断调查表中定义一些信息作为快照信息,例如故障发生时间、电压、行驶里程数、车速等。在对应故障发生时,ECU端要记录发生故障时的快照信息,客户端可以通过此服务检索客户端定义的DTCMaskRecord的捕获DTC Snapshot记录号以及DTCSnapshot记录数据。通过查找故障发生时刻的这些数据,来分析故障原因。

诊断协议那些事儿's blog 5143

把 NAST 变成你的遥控器:用 ABAP 程序精确触发与处理 Output Type

摘要 SAP中的Message Control机制通过NAST表记录输出状态,支持自动触发打印、邮件等操作。关键步骤包括: 构造NAST记录:定义输出类型、介质、处理状态等参数 调用处理函数:使用RV_MESSAGE_UPDATE_SINGLE更新表,通过WFMC_MESSAGES_PROCESS执行输出 典型应用场景: 单据补发(如PO邮件重发) 外部系统集成触发输出 故障恢复处理积压消息 注意事项: 需提交事务确保更新生效 区分S/4HANA新旧输出体系 正确处理派发时间(VSZTP)和状态(VSTAT

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 255

Java多线程 JMM Java内存模型

文章目录为什么需要JMM什么是JMMJMM是并发工具类和并发关键字的底层原理关于JMM最重要的三点内容 为什么需要JMM 由于c语言是不存在内存模型的概念的. 因此它的执行是依赖于处理器的, 导致了不同的处理器的处理结果不一样, 也就无法保证并发安全. 因此需要一个标准, 让多线程的运行结果达到可预期的状态. 不会再出现不同的处理器运行的结果不一致的情况. 什么是JMM JMM : Java Memory Model JMM 是一组规范, 需要各个JVM的实现来遵循JMM规范, 以便于开发者利用

Java持续实践 501

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

学Java多线程必知的Java内存模型JMM

可见性原理之JMM内存模型 JVM与JMM 不要把JVM内存模型与JMM内存模型搞混淆了 我们常说的JVM内存模式指的是JVM的内存分区,而Java内存模式是一种虚拟机规范,是由JVM来具体实现的 Java虚拟机规范中定义了Java内存模型(Java Memory Model,JMM),用于屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的并发效果 JMM规范了Java虚拟机与计算机内存是如何协同工作的:规定了一个线程如何和何时可以看到由其他线程修改过后的共享变量的值,以

JunsIr的技术栈 467

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

【Java多线程】内存模型JMM—主内存与工作内存分析

java内存模型(java memory model): 描述了java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取出变量这样的底层细节。从上图可以得出结论:JMM关于synchronized的两条规定:JVM将内存为和两个部分。主内存: 主要包括和 工作内存: 都有一个工作内存,工作内存中主要包括两个部分,一个是属于和 (包括程序计数器PC和cup工作的高速缓存区)。 线程执行的时候,将首先从,再中,然后传给执行,执行完毕后再给,随后,主存中的值才更新。 在这个过

qq_43842093的博客 3208

java多线程(三)—— java内存模型JMM

java多线程(三)—— java内存模型JMM   此篇文章准备研究的就是java中的JMM,也就是java内存模型(Java Memory Model),那到底为什么要有JMM?什么又是JMM呢?它又有什么作用?它和java内存结构又有什么样的区别? 1、cpu缓存   要知道为什么有JMM,JMM是什么?我们得要从cpu缓存模型说起,当我们介绍完cpu缓存模型,并且知道了cpu缓存模型中所会出现的问题以后,对于JMM就会有很直观的认识,那cpu缓存模型长啥样呢?请由我慢慢道来。 1.1、cpu缓存模型

nianqingren_的博客 335

JAVA基础面试题:Java中的内存模型(JMM)与多线程可见性问题

Victor:Java内存模型(JMM)是Java虚拟机(JVM)定义的一种规范,用于描述多线程环境下,线程如何与主内存和工作内存交互,以及如何保证线程之间的可见性、有序性和原子性。定义线程间通信规则:JMM规定了线程如何通过主内存共享数据。解决可见性问题:确保一个线程对共享变量的修改对其他线程可见。解决有序性问题:防止指令重排序导致程序行为异常。Victor:多线程的可见性问题是指一个线程对共享变量的修改,其他线程无法立即看到。工作内存与主内存的延迟。

qq_61302385的博客 1016

Java多线程内存模型(JMM)

CUP多核并发缓存架构 CPU缓存一致性协议MESI CPU高速缓存(Cache Memory) CPU在摩尔定律的指导下以每18个月起一番的速度在发展,然而内存和硬盘的发展速度远远不及CPU。这就造成了高性能能的内存和硬盘价格及其昂贵。然而CPU的高度运算需要高速的数据。 CPU在摩尔定律的指导下以每18个月起一番的速度在发展,然而内存和硬盘的发展速度远远不及CPU。这就造成了高性能能的内存和硬盘价格及其昂贵。然而CPU的高度运算需要高速的数据。 为了解决这个问题,CPU厂商在CPU中内置了少量的高速

Hello World 2026

Java并发-volatile与JMM多线程内存模型

通过实例运行效果,介绍Java多线程内存模型JMM

字母哥博客 644

java多线程之一/Java内存模型(JMM)

在讲解之前,先区别两个概念:java内存模型与JVM内存模型。java内存模型:JMM(Java Memory Model),JMM的目的是为了解决Java多线程对共享数据的读写一致性问题,通过Happens-Before语义定义了Java程序对数据的访问规则,修正之前由于读写冲突导致的Cache数据不一致的问题。这是一种逻辑抽象,并没有对应内存实体。它规范了(本文将重点讲解)JVM内存模型:是指...

weixin_43212037的博客 239

多线程并发之java内存模型JMM

多线程概念的引入是人类又一次有效压寨计算机的体现,而且这也是非常有必要的,因为一般运算过程中涉及到数据的读取,例如从磁盘、其他系统、数据库等,CPU的运算速度与数据读取速度有一个严重的不平衡,期间如果按一条线程执行将会在很多节点产生阻塞,使计算效率低下。另外,服务器端是java最擅长的领域,作为服务器必须要能同时响应多个客户端的请求,同样需要多线程的支持。在多线程情况下,高并发将带来数据的共享与竞

seaboat——a free boat on the sea.(公众号:远洋号) 3785

java线程内存模型_多线程之Java内存模型(JMM)(一)

在未正确使用锁的时候,多线程的程序可能变的很容易出错,并且难以排查。而JMM则给我们一种规范,它描述了多线程程序如何与内存交互。JMM大致描述:JMM描述了线程如何与内存进行交互。Java虚拟机规范视图定义一种Java内存模型,来屏蔽掉各种操作系统内存访问的差异,以实现Java程序在各种平台下都能达到一致的访问效果。JMM描述了JVM如何与计算机的内存进行交互JMM都是围绕着原子性,有序性和可见性...

weixin_42511702的博客 175

Java 多线程--内存模型(JMM)

文章目录一、为什么要有内存模型二、什么是Java内存模型JMM定义的规范是什么样的三、Java内存模型的具体实现 一、为什么要有内存模型 计算机在执行程序的时候,每条指令都是在CPU中执行的,而计算机的数据是存储在内存当中的,因此CPU在执行的过程中会不断读取在内存当中的数据。 但是随着硬件的发展,CPU的速度越来越快,但是内存的速度没有发生太大的改变 所以CPU在执行程序的时候,很大一部分的时候都在等候内存中的数据。 为了减少CPU等待内存中的数据,引入了高速缓存的概念。 程序在运行的过程中,会将需要运到

Dopezhi Blog 291

Java 内存模型JMM 多线程问题总结

JMM(Java内存模型)中, 多线程下访问共享资源(例如共享变量)时,会遇到的三个问题的解决: 1.原子性(线程安全): (1).对于基本数据类型的读写操作,本身就是原子性的,不存在线程安全的问题 (2).除了JVM自身提供的对基本数据类型读写操作的原子性外,对于方法级别或者代码块级别的原子性操作,可以使用synchronized关键字或者重入锁(ReentrantLock),以及其它各种锁,保证程序执行的原子性 2.可见性: JMM把内存工作内存与主内存同步延迟现象导致的可见性问题,可以使用synch

wdquan19851029的专栏 987
上一篇: 线程优先级
下一篇: 线程安全
Art&Code
博客等级 码龄7年 77粉丝 554原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值