Java教程

深入了解jvm-2Edition-Java内存模型与线程

本文主要是介绍深入了解jvm-2Edition-Java内存模型与线程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

1、概述

  现代计算机中,CPU的运算速度远远快于内存存取速度,为了更好利用CPU的硬件性能,人们想出来很多办法。

  1、减小CPU和内存的速度差距——cache

    通过在CPU和内存之间加入一层缓冲层,CPU的输出输入到cache中,输入从cache中获得。

    然后再利用局部性原理,预先在cache中存入更多数据,来满足CPU的高速处理。

    cache命中率、cache缺失、cache一致性、写缓冲、写回、写直达。

  2、减少CPU等待——多任务处理

    在当前任务在执行IO操作时,采用DMA方式执行IO,CPU保存当前进程的工作现场,并切换到其他进程执行。

  在多处理器的CPU中,每个处理器都有自己的高速缓存,这时就引出了一个问题——缓存一致性(内存可见性)。

  当多个处理器都对同一数据进行处理时,数据在处理器的缓存中可能会产生多个副本。

  Java虚拟机规范中试图定义一种统一的Java内存模型来屏蔽掉硬件和操作系统内存访问的差异,从而在内存访问上实现平台无关性。

  Java内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量储存到内存和从内存取出的底层细节。

2、Java内存模型

  2.1 主内存与工作内存

    Java内存模型规定所有的变量都存储在主内存中。

    每个线程有自己的工作内存,其中保存了该线程使用到的变量的主内存副本拷贝(引用)。

    线程对变量的所有操作(读取、赋值)都必须在工作内存中进行,而不能直接读写主内存中的变量。

    线程间的变量值的传递均需要通过主内存来完成。

  2.2 内存间交互操作

    如何在主内存和工作内存之间传递数据?

    Java内存模型规定了8种操作:

      1、lock

        作用于主内存的变量,把变量标识为一条线程独占的状态。

      2、unlock

        将主内存变量从锁定状态释放。释放后的变量才能被其他线程锁定。

      3、read

        作用于主内存变量,把变量值从主内存传输入工作内存,以便后面的load操作使用。

      4、load

        作用于工作内存的变量,把read操作输入的值放入工作内存的变量副本中。

      5、use

        作用于工作内存变量,将工作内存变量值传递给执行引擎(当虚拟机执行到需要变量值的指令时)。

      6、assign

        作用于工作内存变量,把从执行引擎收到的值赋给工作内存变量。

      7、store

        作用于工作内存变量,把变量值传送到主内存中。以便后面的write指令使用。

      8、write作用于主内存变量,把store操作传过来的值放入主内存的变量中。

    执行以上8种操作需要满足以下规则:

      1、read和load,store和write必须配对出现。不能存在输入但不接收的情况。

      2、不允许一个线程丢弃它最近的assign操作,变量在工作内存中改变后必须同步回主内存。

      3、不允许一个线程无原因(没有改变)地将数据同步回主内存。

      4、新的变量只能在主内存中产生,使用变量前,必须经过assign和load操作。

      5、一个变量同一时刻只能有一个线程对其进行lock操作。但是,可以被一个线程多次执行lock操作(重入)。

      6、如果对变量执行lock操作,那么会清空工作内存中变量的值,使用前需要重新load或赋值。

      7、unlock必须在lock之后才能进行。

      8、对变量执行unlock之前,必须同步回内存。

    主内存--->工作内存:read--->load  按先后顺序执行,没有原子性要求。

    工作内存--->主内存:store--->write 按先后顺序执行,没有原子性要求。

    对于volatile类型变量的特殊规则:

      1、可见性

        一个线程修改了变量值,其他线程立即可以知道这个修改。

        没有保证原子性。因此,read->change->write的操作(依赖当前值)是不安全的。

      2、禁止指令重排序

        在本地代码中插入了许多内存屏障来保证处理器不发生乱序执行。

    对于long、double类型的特殊规则:

      非原子协定:对于64位数据类型的读写可以采用两条32位指令来实现,且不强制两条指令的原子性。

  2.3 先行发生规则

  

3、Java与多线程

  线程是比进程更轻量级的调度执行单位,线程的引入,可以把进程的资源分配和调度执行分开。

  各个线程既可以共享进程资源(内存、IO),又可以独立的被调度运行。 

  3.1 线程实现

    1、使用内核线程实现

      内核线程(Kernal-Level Thread):直接由操作系统内核支持和管理的线程。

      轻量级进程(Light Weight Process):内核线程的接口,用于程序调用。

      使用内核线程实现就是在程序中调用轻量级进程接口,将线程的实现交给操作系统。

      LWP:KLT=1:1。

      线程切换代价太高,支持的线程数量有限。

    2、使用用户线程实现

      自己在程序中实现线程的建立、同步、销毁和调度。

      切换效率高,实现复杂。

    3、使用用户线程+内核线程 的混合模式

      创建、切换销毁自己实现,调度和处理器映射则依赖操作系统。

      UT(user thread):LWP:KLT=m:n:n。

      折中。

  3.2 Java线程实现

    Linux和windows都是1:1的实现。

4、Java线程调度

  抢占式和非抢占式。

  Java使用抢占式。

  线程优先级,Java有10个等级,但是最终都要映射到操作系统的优先级上。

  Windows有7个等级。

  Java线程状态:

    1、New

      创建后尚未启动。

    2、Runable

      包括操作系统中的Running和Ready状态。

    3、Waiting

      无限期地等待,直到被显示地唤醒。

      没有设Timeout参数的Object.wait()方法和Thread.join()方法;

      LockSupport.park() 方法。

    4、Timed Waiting

      一段时间后自动苏醒。

      Thread.sleep()方法;

      设置了Timeout参数的Object.wait和Thread.join方法;

      LockSupport.parkNanos()方法;

      LockSupport.parkUtil()方法。

    5、Blocked

    6、Terminated

 

    

    

    

这篇关于深入了解jvm-2Edition-Java内存模型与线程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!