并发第二弹 Java内存模型

物理计算机内存模型

在讲Java虚拟机的内存模型之前,先来介绍一下物理计算机的内存模型,如下图所示。

并发第二弹 Java内存模型


由于处理器需要与内存交互,因此IO操作是很难消除的,而处理器与内存的运算速度有着几个数量级的差距,因此引入一层或多层运算速度尽可能接近处理器的高速缓存来作为内存与处理器之间的缓冲。


处理器的乱序执行:

  • 除了增加高速缓存之外,为了使处理器内部的运算单元能尽量被充分利用,处理器可能会对输入代码进行乱序执行优化(类似于Java中的指令重排序)。


Java内存模型(Java Memory Model,JMM)


Java内存模型如下图所示。

并发第二弹 Java内存模型


Java内存模型的8种操作(每一种操作都是原子性的):

  • lock(锁定)

  • unlock(解锁)

  • read(读取)

  • load(载入)

  • use(使用)

  • assign(赋值)

  • store(存储)

  • write(写入)


如何使用这8种操作:

  • 如果要把一个变量从主内存拷贝到工作内存,那就要按顺序执行read和load操作;

  • 如果要把变量从工作内存同步回主内存,就要按顺序执行store和write操作;

  • 注意,Java内存模型只要求上述两个操作必须按顺序执行,但不要求是连续执行,比如可以是read read load load。


关于这8种操作的详细介绍与使用规则我就不做深入讲解了,能够理解Java内存模型即可。


Java内存模型的三个特性:

  • 原子性:首先8种操作都是原子性的,其次,如果需要更大范围的原子性保证,则由lock和unlock操作保证。Java提供字节码指令monitorenter和monitorexit来隐式地使用这两个操作,而这两个字节码指令反映到Java代码中就是synchronized关键字;

  • 可见性:volatile保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。除了volatile之外,Java还有两个关键字能实现可见性,分别是synchronizedfinal。synchronized的可见性因为对一个变量执行unlock操作之前必须先把此变量同步回主内存中(执行store、write操作;

  • 有序性:volatile禁止指令重排序,synchronized由于是串行执行因此是有序的;


什么是long和double的非原子性协定?

  • 对于64位的数据类型(long和double),允许虚拟机将没有被volatile修饰的64位数据的读写操作划分为两次32位的操作来进行,即允许虚拟机自行选择是否要保证64位数据类型的load、store、read和write这四个操作的原子性,这就是所谓的long和double的非原子性协定;

  • 64位虚拟机不会出现非原子性访问,32位虚拟机有这个风险,但一般情况下也不需要用volatile来修饰long和double型变量;


操作系统线程与Java线程的关系?


我们知道在Java中启动一个线程是用Thread类的start的方法,因此我们看一下源码。


public synchronized void start() {
  if (threadStatus != 0)
    throw new IllegalThreadStateException();
  group.add(this);

  boolean started = false;
  try {
    start0();
    started = true;
  } finally {
    try {
      if (!started) {
        group.threadStartFailed(this);
      }
    } catch (Throwable ignore) {
    }
  }
}

private native void start0();


start会调用start0方法,而start0是本地方法。Java虚拟机规范并没有规定Java如何实现线程,以HotSpot为例,它的每一个Java线程都是直接映射到一个操作系统原生线程来实现的,而且中间没有额外的间接结构,HotSpot自己是不会去干涉线程调度的(可以设置线程优先级给操作系统提供调度建议),全权交给底下的操作系统去处理,何时冻结或唤醒线程、该给线程分配多少处理器执行时间、该把线程安排给哪个处理器核心去执行等都是由操作系统完成的。

一句话总结就是Java线程就是操作系统的原生线程


线程调度方式:

  • 协同式线程调度:由线程自己控制

  • 抢占式线程调度:由操作系统控制

由于Java线程是操作系统原生线程,因此Java的线程调度是抢占式线程调度。


原文始发于微信公众号(初心JAVA):并发第二弹 Java内存模型

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/35557.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!