文章目录
1. 进程
1.1 进程的概念
进程是操作系统对一个正在运行的程序的一种抽象,换言之,可以把进程看做程序的一次运行过程。当一个程序被执行时,操作系统会为该程序创建一个进程,进程是程序执行过程中的一个实体,它包括程序的代码、数据、堆栈和其他与程序执行相关的资源。
1.2 进程的描述—进程控制块抽象(PCB Process Control Block)
在操作系统中如何管理进程?
- 先描述:使用类/结构体,把被管理的对象,各个属性都描述出来(系统中专门使用的是结构体来描述进程的各个属性,这个结构体称为PCB)
- 再组织:使用数据结构,类似双向链表来组织多个PCB
进程控制块(Process Control Block)的缩写,也称为进程描述符(Process Descriptor),是操作系统中用来管理和维护进程信息的数据结构。
PCB存储了一个进程的各种重要信息,包括但不限于:
- 进程状态(运行、就绪、阻塞等)和优先级
- 程序计数器(Program Counter,PC):指向当前正在执行的指令的地址
- 寄存器信息:包括通用寄存器、堆栈指针等
- 进程ID(PID,进程标识符)和父进程ID
- 进程所占用的资源信息,如内存分配情况、打开的文件列表等
- 进程调度信息,如优先级、时间片大小等
PCB是操作系统进行进程调度和管理的基础,当操作系统需要切换进程时,会保存当前进程的上下文信息到其对应的PCB中,然后加载下一个将要执行的进程的PCB,并将其上下文信息恢复,从而实现进程的切换。PCB的创建和维护由操作系统负责。
2. 线程(Thread)
2.1 概念
线程-----“轻量级进程”,线程不能独立存在,必须依附于进程(进程包含线程,可以是一个也可以是多个)。
2.1.1 线程出现的原因
并发编程成为刚需。虽然多进程也能实现 并发编程,但是由于进程太重量,效率不高,创建进程、销毁进程、进程调度时间的消耗较多,主要消耗在申请资源, 线程比进程更轻量,线程之间共享进程的内存空间和硬盘资源,创建线程,不需要重复去申请资源,省去了这一部分的开销 ,提高了系统的效率和资源利用率。
2.1.2 线程的创建
创建线程需要两个步骤,首先,明确线程要执行的任务。其次,调用系统API创建出线程。
方法1 继承 Thread 类
- 继承 Thread 来创建一个线程类
class MyThread extends Thread {
@Override
public void run() {
System.out.println("这里是线程运行的代码");
}
}
- 创建 MyThread 类的实例
- 调用 start 方法启动线程
MyThread t = new MyThread();
t.start();//真正调用系统API,在系统中创建线程,再让线程调用run
方法2 实现 Runnable 接口
- 实现 Runnable 接口
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("这里是线程运行的代码");
}
}
- 创建 Thread 类实例, 调用 Thread 的构造方法时将 Runnable 对象作为 target 参数.
- 调用 start 方法
Thread t = new Thread(new MyRunnable());
t.start();
对比上面两种方法:
- 继承 Thread 类, 直接使用 this 就表示当前线程对象的引用.
- 实现 Runnable 接口, this表示的是 MyRunnable 的引用. 需要使用 Thread.currentThread()
方法3 匿名内部类创建 Thread 子类对象
// 使用匿名类创建 Thread 子类对象
Thread t1 = new Thread() {
@Override
public void run() {
System.out.println("使用匿名类创建 Thread 子类对象");
}
};
方法4 匿名内部类创建 Runnable 子类对象
// 使用匿名类创建 Runnable 子类对象
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("使用匿名类创建 Runnable 子类对象");
}
});
方法5 lambda 表达式创建 Runnable 子类对象
// 使用 lambda 表达式创建 Runnable 子类对象
Thread t3 = new Thread(() -> System.out.println("使用匿名类创建 Thread 子类对象"));
Thread t4 = new Thread(() -> {
System.out.println("使用匿名类创建 Thread 子类对象");
});
2.3 线程的状态
- NEW: 安排了工作, 还未开始行动(Thread对象已经有了,start方法还没有被调用)
- RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作. (线程已经在CPU上执行了或者线程正在等待在CPU上执行)
- BLOCKED: 这几个都表示排队等着其他事情(由于锁竞争产生的阻塞)
- WAITING: 这几个都表示排队等着其他事情(不固定时间的方式产生的阻塞)
- TIMED_WAITING: 这几个都表示排队等着其他事情(固定时间产生的阻塞)
- TERMINATED: 工作完成了. (内核中的线程已经运行结束了,但是Thread对象还存在)
3. 进程和线程的区别
-
进程包含线程,一个进程可以有一个线程,也可以有多个线程。每个进程至少有一个线程存在,即主线程。
-
进程和线程都可以实现并发编程,但是线程更加轻量、高效。
同一个进程中的线程共用一份内存资源和硬盘资源,省去了申请资源的开销。 -
由于进程间的独立性,一个进程的崩溃不会影响其他进程。
线程之间共享相同的内存空间,一个线程的错误可能会影响整个进程的稳定性。 -
进程拥有独立的地址空间和资源,每个进程之间相互独立,通信需要通过进程间通信机制来实现;而线程在同一进程中共享资源,可以直接访问进程中的所有数据,线程之间的通信更加方便。
-
由于进程拥有独立的内存空间,进程切换的开销通常比线程切换的开销更大。
线程切换通常只涉及线程上下文的切换,而不涉及内存空间的切换。 -
进程是系统资源分配的最小单位,线程是系统调度的最小单位。