内卷老员工之java内存模型

java内存模型

java内存模型都不知道,如何内卷老员工

  • Java 内存模型指 Java 虚拟机如何使用计算机的内存。
  • Java 内存模型指定不同线程如何以及何时可以看到其他线程写入共享变量的值,以及如何在必要时同步对共享变量的访问。

内部java内存模型

  • jvm内部适用的java内存模型在堆和线程栈之间分配内存。

image.png

  • java虚拟机中每个线程都有自己的线程堆栈,当线程执行代码时,堆栈会发生变化。
  • 线程堆栈还包含每个正在执行的方法(调用堆栈上的所有方法)的所有局部变量。一个线程只能访问它自己的线程堆栈,每个线程也有自己的局部变量版本。
  • 所有原始类型的局部变量 都完全存储在线程堆栈中,因此对其他线程不可见。一个线程可以将原始变量的副本传递给另一个线程,但它不能共享原始局部变量本身。
  • 堆包含了所有线程创建的对象。

image.png

  • 堆上的对象可以被所有引用该对象的线程访问。当一个线程可以访问一个对象时,它也可以访问该对象的成员变量。如果两个线程同时调用同一个对象的方法,它们都可以访问对象的成员变量,但每个线程都有自己的局部变量副本。
public class MyRunnable implements Runnable() {

    public void run() {
        methodOne();
    }

    public void methodOne() {
        int localVariable1 = 45;

        MySharedObject localVariable2 =
            MySharedObject.sharedInstance;

        //... do more with local variables.

        methodTwo();
    }

    public void methodTwo() {
        Integer localVariable1 = new Integer(99);

        //... do more with local variable.
    }
}
复制代码
public class MySharedObject {

    //static variable pointing to instance of MySharedObject

    public static final MySharedObject sharedInstance =
        new MySharedObject();


    //member variables pointing to two objects on the heap

    public Integer object2 = new Integer(22);
    public Integer object4 = new Integer(44);

    public long member1 = 12345;
    public long member2 = 67890;
}
复制代码
  • 如上代码所示,每个线程执行methodOne()会创建自己的副本,localVariable1和localVariable2在各自的线程中。静态变量只有一个副本,因此两个线程的localVariable2指向堆中的同一个静态变量副本。而localVariable1则存储在各自的栈中。
  • methodTwo()方法中,每个线程都在堆中创造了一个对象,并指向各自的对象。

硬件内存架构

  • 硬件内存架构与java内部内存架构有所不同,如下为硬件内存架构的简化图

image.png

  • 计算机通常有多个cpu,每个cpu中都有自己的寄存器,寄存器执行操作的速度比内存快得多。
  • 每个cpu还有自己的cpu缓存,cpu访问缓存的速度同样高于访问主内存的速度,但通常不如访问寄存器速度快。
  • 计算机还有主内存区,所有cpu都可以访问主内存,主内存比cpu内存大得多。
  • 当cpu需要访问主内存时,会将一部分主内存内容读到cpu缓存,甚至一部分读到cpu的寄存器中。当cpu需要将内容写到主内存时,会先将内容写到寄存器,再写到缓存中,最后在适当时间将值刷新到主内存。
  • 当cpu需要在高速缓存中存储其他内容时,存储在高速缓存中的值通常会刷新回主内存。cpu可以将数据写入部分内存并刷新部分内存,不必读写完整缓存。通常,缓存在称为“缓存行”的较小内存块中更新。一条或多条高速缓存线可被读入高速缓存存储器,并且一条或多条高速缓存线可再次刷新回主存储器。

java内存模型与硬件内存模型之间的桥梁

  • java内存模型与硬件内存架构是不同的,硬件内存架构中不区分堆和栈。堆和栈中的数据可能存在于主内存、cpu高速缓存及cpu寄存器中。
  • 当对象和变量可以存储在硬件的不同区域时,可能会面临以下问题:
    • 线程更新、写入共享变量的可见性。
    • 读取、检查和写入共享变量时的竞争条件。

共享对象的可见性

  • 如果两个或多个线程共享对象,没有正确的使用volatile,则一个线程所做的更新对其他线程不可见。
  • 共享对象最初存储在主存中,在cpu上运行的线程将其读取到cpu的寄存器及缓存中。只要尚未将其更新刷新回主内存,则对象的更新版本对其他线程不可见。每个线程都拥有自己的副本,位于自己的cpu缓存中。
  • 要解决这个问题,可以适用volatile关键字。该volatile 关键字可以确保直接从主内存中读取给定变量,并在更新时始终将其写回主内存。

竞争条件

  • 当多个线程更新同一个共享对象或变量,则可能出现竞争条件。
  • 如果线程a和线程b同时读取主内存中为0的共享变量count,两个线程各自执行了+1的操作。如果顺序执行则变量增加了2,如果同时执行最终的结果可能仅仅是+1。
  • 为解决此问题,可以使用同步代码块,确保同一时间只有一个线程操作该变量。同步块还保证所有在同步块内访问的变量都会从主存中读入,并且当线程退出同步块时,所有更新的变量都会再次刷新回主存,无论变量是否声明为 volatile 或不是。

尾记

  • 千古兴亡多少事?悠悠。不尽长江滚滚流。
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享