1、线程运行原理

1.1 栈与栈帧  

Java Virtual Machine Stacks (Java 虚拟机栈 JVM)

我们都知道 JVM 中由堆、栈、方法区所组成,其中栈内存是给谁用的呢?其实就是线程,每个线程启动后,虚拟机就会为其分配一块栈内存。

  • 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存

  • 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

单线程示例代码

public class TestFrames {
   public static void main(String[] args) {
       method1(10); // 断点处
  }

   private static void method1(int x) {
       int y = x + 1;
       Object m = method2();
       System.out.println(m);
  }

   private static Object method2() {
       Object n = new Object();
       return n;
  }
}

在打断点处,可以看到一个栈帧

执行到method1,可以看到新起了一个栈帧

当执行到method2时,可以看到又新起了一个栈帧

由于是栈,随着的程序的运行,后面开启的栈帧会先被销毁,直至main栈帧被销毁,此刻程序运行完成。

对应图解:

内存释放后

具体就是:

1.将编译好的字节码加载到jvm的方法区内存中
2.jvm启动一个main的主线程,cpu核心就准备运行主线程的代码了,给主线程分配自己的栈内存【args、局部变量、返回地址、所记录】,每个线程的栈里面还有个程序计数器
程序计数器的作用:当cpu要执行哪行代码了,就去这个里面去要
3.把主方法的里面代码行放到程序计数器
4.主方法调用的是method1的方法,为method1分配栈内存,里面存储这个方法里面局部变量,返回地址,这些变量是分配内存时,会把空间预留好
5.将method1的第一行读到程序计数器让cpu执行
6.methode1下一行调用method2()方法,创建他的栈内存
7.把Object n = new Object()这行代码读取到计数器,在队中创建对象
8.method2()将返回地址给m,方法执行完就可以释放掉method2()的栈内存
9.一层层方法结束后,依次释放掉每个方法线程

现在来看看多线程下的栈与栈帧

public class TestFrames {
   public static void main(String[] args) {
       Thread t1 = new Thread(){
           @Override
           public void run() {
               method1(20);// 断点处
          }
      };
       t1.setName("t1");
       t1.start();
       method1(10);// 断点处
  }

   private static void method1(int x) {
       int y = x + 1;
       Object m = method2();
       System.out.println(m);
  }

   private static Object method2() {
       Object n = new Object();
       return n;
  }
}

在第一个断点处

可以看到多个线程同时运行中,我们可以选择具体的线程来查看运行状况并且往下运行,具体的读者可以自行实践。

1.2 线程上下文切换(Thread Context Switch)

因为以下一些原因导致 cpu 不再执行当前的线程,转而执行另一个线程的代码(简单来说就是从使用cpu到不使用cpu

  • 线程的 cpu 时间片用完

  • 垃圾回收

  • 有更高优先级的线程需要运行

  • 线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法

当 Context Switch(上下文切换) 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java 中对应的概念就是程序计数器(Program Counter Register),它的作用是记住下一条 jvm 指令的执行地址,是线程私有的

  • 状态包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等

  • Context Switch 频繁发生会影响性能,因为线程数不是越多越好。

Java并发(四)----线程运行原理的更多相关文章

  1. java并发包&线程池原理分析&锁的深度化

          java并发包&线程池原理分析&锁的深度化 并发包 同步容器类 Vector与ArrayList区别 1.ArrayList是最常用的List实现类,内部是通过数组实现的, ...

  2. Java 并发编程 | 线程池详解

    原文: https://chenmingyu.top/concurrent-threadpool/ 线程池 线程池用来处理异步任务或者并发执行的任务 优点: 重复利用已创建的线程,减少创建和销毁线程造 ...

  3. java并发编程 线程基础

    java并发编程 线程基础 1. java中的多线程 java是天生多线程的,可以通过启动一个main方法,查看main方法启动的同时有多少线程同时启动 public class OnlyMain { ...

  4. Java 并发 中断线程

    Java 并发 中断线程 @author ixenos 对Runnable.run()方法的三种处置情况 1.在Runnable.run()方法的中间中断它 2.等待该方法到达对cancel标志的测试 ...

  5. 《java学习三》并发编程 -------线程池原理剖析

    阻塞队列与非阻塞队 阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞.试图从空的阻塞队列中获取元素的线程将会被阻塞,直到 ...

  6. Java并发——ThreadPoolExecutor线程池解析及Executor创建线程常见四种方式

    前言: 在刚学Java并发的时候基本上第一个demo都会写new Thread来创建线程.但是随着学的深入之后发现基本上都是使用线程池来直接获取线程.那么为什么会有这样的情况发生呢? new Thre ...

  7. Java并发编程——线程池的使用

    在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统 ...

  8. Java并发集合的实现原理

    本文简要介绍Java并发编程方面常用的类和集合,并介绍下其实现原理. AtomicInteger 可以用原子方式更新int值.类 AtomicBoolean.AtomicInteger.AtomicL ...

  9. java并发:线程池、饱和策略、定制、扩展

    一.序言 当我们需要使用线程的时候,我们可以新建一个线程,然后显式调用线程的start()方法,这样实现起来非常简便,但在某些场景下存在缺陷:如果需要同时执行多个任务(即并发的线程数量很多),频繁地创 ...

  10. java并发:线程同步机制之Lock

    一.初识Lock Lock是一个接口,提供了无条件的.可轮询的.定时的.可中断的锁获取操作,所有加锁和解锁的方法都是显式的,其包路径是:java.util.concurrent.locks.Lock, ...

随机推荐

  1. (Linux服务器)git添加SSH公钥后本地验证失败

    环境:腾讯云Ubuntu x86_64 问题说明: 在配置了公钥后,一直提示我git@github.com: Permission denied (publickey). 解决办法: 先查看root/ ...

  2. 解决Python写入yaml后排版混乱还丢失注释问题

    转载https://www.cnblogs.com/jiahm/category/1530828.html 大家有没有遇到过在使用Python进行yaml文件写入数据后,内容排版混乱并且丢失注释问题, ...

  3. Matlab - 在Figure界面去掉图像的坐标刻度

    Matlab版本:2018b 经过一番尝试,发现有两种方法 第一种:修改坐标轴的Visible属性,去掉坐标轴数字和坐标轴标签 第二种:删除Tick,只去掉坐标轴数字 第一种 ①原图 ②如果有多个子图 ...

  4. k8s namespace kubeDNS

    图中kube-dns只是一个service,但是他对外提供k8s集群内部的dns服务,真正的dns server,是 coredns这几个pod k8s namespace 的作用只是提供逻辑上的组件 ...

  5. ANOMALY: use of REX.w is meaningless (default operand size is 64)

    1.打开注册机:win+ R   输入regedit2.找到目录:计算机\HKEY_LOCAL_MACHINE\SOFTWARE\TEC\Ocular.3\agent\config 并添加值3.新增项 ...

  6. Javaweb学习第十二弹--Request和Response

    XML配置方式编写Servlet 3.0版本之前,仅仅支持XML配置文件的配置方式 1.编写Servlet类 2.在web.xml中配置该Servlet Request和Response Reques ...

  7. java异常--基础内容

    java异常--基础内容 异常指程序运行中出现的不期而至的各种状况:文件找不到,网络连接失败,非法参数... 异常发生在程序运行期间,他影响了正常的程序执行流程. 三种类型异常(Exception): ...

  8. Linux RedHat 利用 ISO镜像文件制作本地 yum源

    RedHat 利用ISO镜像文件制作本地yum源 [1]创建iso存放目录和挂载目录 1 [root@desktop ~]# cd /mnt/ 2 [root@desktop mnt]# mkdir ...

  9. Linux耳机音响独立输出

    记得之前在用Ubuntu系统的时候,耳机和音响(线缆输出和模拟耳机输出)只能单独插一个,如果两个设备同时插入主机,将会导致只能耳机输出,即使选择后置音响(线缆输出)也不会有声音.在Windows下,即 ...

  10. 全面了解 Redis 高级特性,实现高性能、高可靠的数据存储和处理

    目录 高性能.高可用.高可扩展性的原理 持久化 RDB持久化 AOF持久化 持久化的配置 RDB配置 AOF配置 持久化的恢复 RDB的恢复 AOF的恢复 RDB和AOF的选择 持久化对性能的影响 数 ...