JVM 内存模型

47 阅读4分钟

前言

java内存模型这个概念本身就是一个抽象的,不是真是存在。它是逻辑上的一个抽象概念。它描述了一组规则或者规范。通过这组规范定义了程序中各个变量(实例字段、静态字段、构成数组对象的元素)的访问方式。

模型图

image.png

JVM运行程序的实体是线程,每个线程创建时JVM都会为其创建一个工作内存(栈内存),用于存放线程私有的数据。

内存模型中规定了所有的变量都存储在主内存,而主内存又是共享的,多有线程都可以访问。

线程拥有私有空间的意义

工作内存存放的数据,只供线程自己使用,其他线程无法访问。

程序计数器:记录程序执行到哪里了,以便CPU切换能够继续执行 局部变量:方法中的变量,只供当前方法使用 方法参数:方法的入参,会记录到内存空间中供当前线程使用

主内存的变量操作的注意事项

变量是多线程共享的,那么在增删改操作时,应当谨慎。为了保证线程的同步性,必须对改共享变量进行加锁操作,保证多线程环境下,所有线程读取到的值都是一致的。 在读操作时,因为不会修改数据,所以所有线程读取的数据都是一致的

生命周期

线程作为一种事物,也是有生命周期,从创建到销毁,它将经历6中状态的变化。

join 方法场景

在多线程环境下,有时候会有这样的场景,某个线程执行结束后才执行另一个线程。针对如此场景,线程的join方法孕育而生。

yield 方法是什么

要知道yield是什么,我们要知道CPU的时间片概念,CPU时间片是操作系统调度算法中的一个概念。操作系统为每一个线程都分配一个时间片来占用CPU。正常情况下,当一个线程把分配给自己的时间片使用完了,线程调度器才会进行下一轮的线程调度。

那么这里的存在一个问题就是,时间片没有用完,是不能进行下一轮的调度的。这其中的就有了yield的出现,它的含义就是告诉CPU我不使用CPU了,你给其他线程吧。

线程的上文切换如何理解

在多线程编程中,线程个数一般都大于CPU个数,而每个CPU同一时刻只能被一个线程使用。 现代CPU的频率都是很高的,在充分利用CPU资源的考虑下,诞生了时间片轮转的调度策略。也就是说:分配给每个线程一个时间片,线程在时间片内占用CPU执行任务。呈现出来的用户效果就是:多线程在同时运行。

为什么出现线程死锁

死锁是因为两个或以上的线程在执行过程中,因争夺资源而造成相互等待的现象,在无外力的作用的情况下,这些线程会一直相互等待下去,而无法运行。

锁的必要条件

  • 互斥:进程要求对所有资源的分配进行排他控制
  • 不可剥夺:进程所获得的资源在未使用完毕前,不能被其他进程强行夺走
  • 请求与保持条件:进程已经保持了至少一个资源,但又提出新的资源请求,然后该资源已被其他进程占用,那么该进程只能阻塞
  • 循环等待:发生死锁时,必然存在一个线程请求资源的环形链。

如何避免死锁

至少破坏掉一个造成死锁的必要条件。但我们知道:只有请求并持有、环路等条件是可以被破坏的

造成死锁的原因其实和申请资源的顺序又很大关系,使用资源申请的有序性原则是可以避免死锁的。

线程分类

java中的线程被分为:守护线程、用户线程

守护线程的定义是:程序运行时的时候后台提供了一种通用的服务线程:垃圾回收等。因此当所有非守护线程结束时,线程就结束,同时会杀死进程中的所有守护线程。

区别:说白了,守护线程的结束,是不会影响JVM退出的。但所有用户线程结束了,那么JVM就会结束,同时结束掉在运行的守护线程。