美文网首页
Java并发(6)——Java内存模型

Java并发(6)——Java内存模型

作者: alexwu59 | 来源:发表于2018-06-26 15:10 被阅读0次

    并发编程两个问题

    多线程之前有两个问题需要解决:线程之间的通信和线程同步。
    线程通信:共享内存和消息传递。共享内存方法是,多个线程通过读写公共内存共享数据,隐式进行线程通信。
    线程同步:控制不同线程操作执行的相对顺序
    java提供的共享内存方式,隐式进行线程之间的消息传递,但是需要程序员显示提供线程之间的同步。

    CPU和缓存一致性

    由于现代CPU速度远远超过了内存速度,为了提高CPU访问内存的速度,CPU和内存之间增加高速缓存Cache。当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。随着CPU技术的不断提升,一级高速缓存已经无法满足需求,逐渐发展出多级缓存——L1,L2,L3。L3是L2的缓存,L2是L1的缓存,L1是cpu的缓存。三级缓存从CPU到内存一级比一级容量大,但是访问速度越来越慢。当CPU要读取一个数据时,首先从一级缓存中查找,如果没有找到再从二级缓存中查找,如果还是没有就从三级缓存或内存中查找。
    对于单核CPU只含有一套L1,L2,L3缓存;

    如果CPU含有多个核心,即多核CPU,则每个核心都含有一套L1(甚至和L2)缓存,而共享L3(或者和L2)缓存。 多核内存架构
    单线程。cpu核心的缓存只被一个线程访问。缓存独占,不会出现访问冲突等问题。

    单核CPU,多线程。进程中的多个线程会同时访问进程中的共享数据,CPU将某块内存加载到缓存后,不同线程在访问相同的物理地址的时候,都会映射到相同的缓存位置,这样即使发生线程的切换,缓存仍然不会失效。但由于任何时刻只能有一个线程在执行,因此不会出现缓存访问冲突。

    多核CPU,多线程。每个核都至少有一个L1 缓存。多个线程访问进程中的某个共享内存,且这多个线程分别在不同的核心上执行,则每个核心都会在各自的cache中保留一份共享内存的缓冲。由于多核是可以并行的,可能会出现多个线程同时写各自的缓存的情况,而各自的cache之间的数据就有可能不同。

    上面提到在在CPU和主存之间增加缓存,在多线程场景下会存在缓存一致性问题。除了这种情况,还有一种硬件问题也比较重要。那就是为了使处理器内部的运算单元能够尽量的被充分利用,处理器可能会对输入代码进行乱序执行处理。这就是处理器优化。

    除了现在很多流行的处理器会对代码进行优化乱序处理,很多编程语言的编译器也会有类似的优化,比如Java虚拟机的即时编译器(JIT)也会做指令重排。

    在CPU和主存之间增加缓存,在多线程场景下就可能存在缓存一致性问题,也就是说,在多核CPU中,每个核的自己的缓存中,关于同一个数据的缓存内容可能不一致。
    缓存一致性问题其实就是可见性问题。而处理器优化是可以导致原子性问题的。指令重排即会导致有序性问题。

    JAVA内存模型

    内存模型

    为了保证共享内存的正确性(可见性、有序性、原子性),内存模型定义了共享内存系统中多线程程序读写操作行为的规范。通过这些规则来规范对内存的读写操作,从而保证指令执行的正确性。它与处理器有关、与缓存有关、与并发有关、与编译器也有关。他解决了CPU多级缓存、处理器优化、指令重排等导致的内存访问问题,保证了并发场景下的一致性、原子性和有序性。

    Java内存模型

    在java中,存放数据的内存主要是分为两块:java对象,类的实例变量,类的静态变量,数组是存放在堆中,堆是java不同线程共享的。java方法中定义的变量,方法参数,异常变量都是存放在java线程栈中,栈是线程独享的。因此JAVA线程直接的通信是通过共享堆内存实现的。也就是说,发生java存储在堆上的数据,都需要考虑多线程访问下的安全问题。

    Java内存模型(JMM):是一种内存模型规范,它屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。JMM定义了java线程之间如何通过共享内存进行通信,它可以控制线程对共享内存的写何时对另外一个线程可见。Java内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程工作内存也称为私有本地内存,线程的工作内存中保存了该线程中是用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行。 JAVA内存模型

    happens-before

    JMM提供了happens-before原则来指定线程之间的可见性,happens-before
    不是强制规定了程序的执行顺序,只是要求最后的结果应该符合happens-before
    原则,程序员可以安装happens-before来实现自己的业务,而不需要担心线程安全问题。
    如果多个线程之间的操作超越了happens-before规定的原则,那需要程序员显示进行线程同步控制。

    • 程序顺序规则:在一个线程内,每个操作都happens-before后该线程的后序操作
    • 监视器规则:对一个锁的解锁,happens-before后面的加锁
    • volatile规则:对一个volatile变量的写happens-before对它的读
    • 传递性规则:A happens-before B,B happens-before C,那么A happens-before C
    • start规则:线程A.start方法的执行happens-before 线程A的所以操作
    • join规则:线程B调用线程A.join方法,A线程中所有操作happens-before A.join方法的返回。

    相关文章

      网友评论

          本文标题:Java并发(6)——Java内存模型

          本文链接:https://www.haomeiwen.com/subject/gbxmyftx.html