JMM:java内存模型

内存模型

  • java内存结构和java内存模型【Java Memory Model, JMM】不同;

  • jmm定义了一套在多线程读写共享数据【成员变量,数组等】时,对数据的可见性,有序性,原子性的规则和保障;

  • JMM:线程的内存【线程私有】和主内存【多线程共享】;

  • synchronized,同步关键字;

    • 加synchronized关键字后,对应的monitor,监视器会对同步区起作用;
    • monitor一共有三部分:Owner,EntryList,WaitSet;
  • 原子性:

    • 在多线程下,不会被其他线程干扰和中断,要么完整执行,要么不执行;
    • 因为cpu的一个并发机制,会导致多线程的指令交错,所引起的问题;
  • 可见性:

    • 线程t在频繁访问主内存中的run值,JIT编译就会将run的值缓存到本地内存(如寄存器或线程栈),减少对主内存的访问;
    • code cache代码缓存中的机器码一旦生成就不可修改,除非触发优化;
    • 当main线程修改主内存中的run值时,但是t线程却永远看不到;
    static boolean run = true;
    Thread t = new Thread(()->{
       while(run) {
       } 
    }, "t");
    t.start();
    thread.sleep(1000);
    run = false;
    
    • Volatile:易变关键字;
      • 可以避免线程自己代码缓冲区查找变量的值,必须到主内存中获取它的值,必须直接操作主内存;
      • 仅能保证可见性,不保证原子性,适合一个线程写,多个线程读;
      • synchronized语句,既可以保证代码块的原子性,也可保证代码块的可见性,缺点:synchroized属于重量级操作,性能相对更低;
  • 有序性:在同一个线程内,jvm会在不影响正确性的情况下,可能调正语句的执行顺序【指令重排】;

    • 在一个线程下是不影响正确定结果的,但是多线程下,可能产生错误;
    • eg:一个线程new 一个对象,但是当先赋值给变量在进行初始化时,另一个线程拿到的对象,可能还没进行初始化;
    • 使用volatile关键字,解决指令重排问题,明确volatile修饰的相对位置;
  • happens-before:

    • 规定了哪些写操作对其他线程的读操作可见;
    • 它是可见性和有序性的规律总结;
      1. 线程对volatile变量的写,对接下来的其他线程对该变量的读可见;
      2. 线程解锁m之前对变量的写,对接下来的对m加锁的其他线程对该变量的读可见【前一个加锁解锁的写值,在相同锁的下的读可见性】;
      3. 线程start前对变量的写,在该线程开始后,对该变量的读是可见的【线程还没开始的写,对开始后的读可见】;
      4. 线程结束前对对边的写,其他线程在得知它结束后是可见的【isAlive(),join()】;
      5. 对线程打断前的写,对于其他线程得知被打断后,对该变量的读可见【主动打断别人,interrupted,然后可以读取它已经写入的值】;
      6. 对变量的默认值,【0,false,null】的写,对其他线程对该变量的读可见【第一次初始化】;
      7. 可见性,具有传递性;
      8. 这里的变量,成员变量或者静态变量;
posted @ 2025-03-20 14:49  烟雨断桥  阅读(39)  评论(0)    收藏  举报