Java EE 进程&线程

JavaEE 进程&线程

文章目录

  • [JavaEE 进程&线程](#JavaEE 进程&线程)
    • [1. 进程](#1. 进程)
      • [1.1 概念](#1.1 概念)
      • [1.2 进程管理](#1.2 进程管理)
      • [1.3 PCB (Process Control Block)](#1.3 PCB (Process Control Block))
    • [2. 线程](#2. 线程)
      • [2.1 概念](#2.1 概念)
      • [2.1 线程与进程的区别](#2.1 线程与进程的区别)
      • [2.3 创建线程](#2.3 创建线程)

1. 进程

1.1 概念

什么是进程?

进程是操作系统对一个正在执行的程序的一种抽象

我们可以打开任务管理器,里面每个正在执行的程序就是一个进程:

: 我们可以认为,程序 = 进程 (跑起来的程序) / 可执行文件(没有运行起来的程序)

而对于进程来说,它们在执行的过程中都需要消耗一定的硬件资源

结论:

  • 进程是系统分配资源的基本单位

  • 内存管理 :每个进程的内存是彼此独立,互不干扰的。通常情况下,进程A不能直接访问进程B的内存 ,这种情况也称为进程独立性

  • 进程间通信:虽然有进程的独立性,但有时候也需要多个进程相互配合完成某个工作,这个时候就要用到进程间通信。进程间通信和进程的"独立性"并不冲突,系统提供一些公共的空间(多个进程都能访问到的),让两个进程借助这种公共空间来交互数据

1.2 进程管理

在操作系统中,进程管理可以分为两个步骤:

  1. 先描述 : 使用PCB将核心属性描述出来
  2. 再组织 : 使用一定的数据结构(在Linux中一般使用链表),把对应进程的PCB串到一起

下面将对PCB进行具体解释~

1.3 PCB (Process Control Block)

PCB 是指操作系统中的进程控制块Process Control Block),也称为任务控制块(Task Control Block)。每个正在运行或等待执行的进程都有一个对应的 PCB

进程的一些重要信息就存储在PCB中:

  • PID(Process ID)进程的身份标识 ,系统会保证同一个机器上,在同一时刻每个进程的pid都是唯一的;

  • 内存指针(Memory Pointers):描述进程使用内存资源的详细情况,如哪里存放数据,哪里存放指令;

  • 文件描述符表(File descriptor table):描述进程使用了的硬盘的相关信息;

  • 优先级(Process Priority):表示进程的相对重要性或优先级。操作系统可以根据进程的优先级来进行调度,以决定哪个进程先执行;

  • 进程状态(Process State):记录进程当前的状态,如运行、就绪,阻塞(等待控制台输入)等。这是操作系统用于判断进程是否可以执行的关键信息;

  • 进程调度(Process Scheduling):针对每个进程占据了cpu多长时间进行一个统计,根据这个统计结果来进一步的调整调度的策略;

  • 上下文(Context) :类似于存档与读档。其中保存上下文 就是把CPU的关键寄存器中的数据保存到内存中(PCB的上下文属性中),而恢复上下文就是把内存中的关键寄存器中的数据加载到CPU的对应寄存器中

PCB扮演着操作系统中的重要角色,通过保存和维护进程的状态和上下文信息,操作系统可以在进程之间进行切换和调度!!

2. 线程

引子 :进程的使用能够解决"并发编程"的问题,但进程本身在进行频繁创建和销毁的时候,开销也比较大(开销主要体现在资源的申请和释放上),而线程能够解决这个问题!

2.1 概念

什么是线程?

一个线程就是一个"执行流",每个线程之间都可以按照顺序执行自己的代码,多个线程之间可以"同时"执行多份代码

线程也可以称为"轻量级进程",在进程的基础上做出了改进,同时,线程在系统中的调度规则和进程是一样的,线程的PCB中也有状态、优先级、上下文、记账信息...

2.1 线程与进程的区别

  • 进程是包含线程的,每个进程至少有一个线程存在,即主线程;

  • 进程和进程之间不共享内存空间,而线程与线程之间共享一个内存空间;

    多个线程的PCB中的内存指针都指向同一个内存空间,这就意味着只有在创建第一个线程的时候需要从系统中分配资源,后续的线程就不必分配,直接共用前面分配的资源就可以,这大大降低了资源频繁申请释放带来的开销!

  • 进程是系统分配资源的基本单位,线程是系统调度的基本单位

  • 进程与进程之间不会相互影响,一个进程挂了一般不会影响其它正在执行的进程,但线程不一样,一旦一个线程挂了很可能会把同个进程中的其它线程一起带走,导致整个进程一起崩溃;

  • 因为共享同一个内存空间,同一个进程中的线程可能会相互干扰,从而造成线程安全问题;

  • 线程也不是越多越好,线程太多调度开销也会很大

2.3 创建线程

创建线程有以下几种方式:

  1. 继承Thread类

    • 先继承Thread来创建一个线程类

      java 复制代码
      class MyThread extends Thread {
          @Override
          public void run() {
              System.out.println("这里是线程运行的代码");
          }
      }
    • 创建MyThread类的实例

      java 复制代码
      Thread myThread = new MyThread();
    • 调用start方法启动线程

      java 复制代码
      myThread,start(); //线程开始运行

    从上述代码可以发现,我们并没有用对象.方法名的方式调用run方法,但却依旧能够执行run方法,关键就在于:

    • 线程在调用start()方法的时候才正式构建
    • run()方法相当于这个线程的入口方法,此处的run方法不需要我们手动调用,而会在线程创建好之后(即调用start方法后)被JVM自动调用执行
  2. 实现Runnable接口

    • 实现Runnable接口

      java 复制代码
      class MyRunnable implements Runnable {
          @Override
          public void run() {
              System.out.println("这是线程运行的代码");
          }
      }
    • 创建Thread类实例,调用Thread的构造方法时将Runnable对象作为target参数

      java 复制代码
      Thread t = new Thread(new MyRunnable());
    • 调用start方法

      java 复制代码
      t.start(); //线程开始运行
  3. 匿名内部类创建Thread子类对象

    java 复制代码
    package demo1;
    
    public class DemoThread3 {
        public static void main(String[] args) {
            Thread t1 = new Thread() {
                @Override
                public void run() {
                    System.out.println("使用匿名类创建Thread子类对象");
                }
            };
            t1.start();
        }
    }
  4. 匿名内部类创建Runnable子类对象

    java 复制代码
    package demo1;
    
    public class DemoThread5 {
        public static void main(String[] args) {
    
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("使用匿名类创建Runnable子类对象");
                }
            });
            t2.start();
        }
    }
  5. 使用lambada表达式创建Runnable子类对象

    java 复制代码
    package demo1;
    
    public class DemoThread5 {
        public static void main(String[] args) {
            Thread t3 = new Thread(() -> {
                System.out.println("这里使用lambda表示式相当于实现Runnable接口并重写了run方法");
            });
            t3.start();
        }
    }

    因为lambda表达式写起来比较简洁,所以我们一般使用lambda表达式来创建线程。

相关推荐
岁忧25 分钟前
(nice!!!)(LeetCode 每日一题) 3363. 最多可收集的水果数目 (深度优先搜索dfs)
java·c++·算法·leetcode·go·深度优先
陌上 烟雨齐3 小时前
Kafka数据生产和发送
java·分布式·kafka
Jinkxs3 小时前
高级15-Java构建工具:Maven vs Gradle深度对比
java·开发语言·maven
有梦想的攻城狮3 小时前
spring中的ApplicationRunner接口详解
java·后端·spring·runner·application
程序视点3 小时前
设计模式之原型模式!附Java代码示例!
java·后端·设计模式
振鹏Dong4 小时前
微服务架构及常见微服务技术栈
java·后端
丶小鱼丶4 小时前
二叉树算法之【中序遍历】
java·算法
摇滚侠5 小时前
Oracle 关闭 impdp任务
java
编程爱好者熊浪6 小时前
RedisBloom使用
java
苇柠6 小时前
Spring框架基础(1)
java·后端·spring