java中进程的详细解析(含实例代码)
作者:驭渊的小故事
一、操作系统
操作系统是计算机中的一个重要软件。
操作系统是一个搞管理的软件:
- 管理各种硬件设备
- 给各种应用程序提供一个稳定的运行环境,
这些应用程序在运行中,即使有一个应用程序发生bug崩溃了,也不会影响其他应用程序的执行。
操作系统中的重要概念:进程
• 运行起来的程序就叫进程。
站在操作系统的视角,如何管理进程?
先描述
使用结构体,描述出进程的核心属性,
进程控制块(PCB),非常大的结构体,有很多个属性。再把多个进程组织起来
比如:Linux这样的操作系统,使用链表这样的方式,把每个PCB串一起。
• 操作系统本身也是一个软件,也是由一系列指令构成的,在启动时,CPU依次执行操作系统上的这些指令(引导操作系统起来)
• 所有的软件本质上都是靠CPU来执行的,CPU具体执行哪些指令,一定程度上受操作系统的影响。
- 文件描述符
进程在运行过程中,很多时候需要和硬盘这个硬件设备进行交互。
硬盘上的数据,就是以文件的形式来组织的。
进程在读写文件的时候,就需要打开文件,每个打开的文件相关的信息保存到文件描述符中,文件的每一项都对应着打开了的一份文件。
操作系统中,会把很多资源抽象成文件来表示,所以“文件”不只是硬盘上的类
例:网卡,操作系统管理网卡的时候,就是当作“文件”一样来管理
二、进程的运行
• 进程的运行,也会依赖到硬盘、网卡等相关硬件设备
• 进程运行,执行指令,都是靠CPU的。
进程是操作系统中,负责分配CPU的基本单位。
分时复用:把一个单位时间,分成很多份,分别执行不同进程的指令
第一份,执行进程1指令
第二份,执行进程2的指令……
因为CPU的运行速度是特别快,所以在切换的过程中,人眼很难察觉出变化了,像同时执行一样。把一个CPU核心上,按照分时复用,执行多个进程,就叫“并发执行”
多个进程在一个CPU核心上在不同单位时间内执行。把多个CPU核心上,同时执行多个进程 称为“并行执行”
多个进程在多个CPU核心上同时执行
进程状态,
- New(新建)
- RUNNABLE(可运行)
- BLOCKED(阻塞)
- WAITING(等待)
- TIMED_WAITING(超时等待)
- TERMINATED(终止)
- 进程有多种状态,下面是两个典型
1)就绪状态 → 随时可以到CPU上执行
2)阻塞状态 → 进程当前不适合到CPU上执行
- 进程有多种状态,下面是两个典型
进程有优先级
为了保证需要大量CPU资源能的正常执行进程的上下文
因为有进程调度,一个进程执行一会,就会让出CPU,这期间CPU执行它会保存上次执行的状态,多进程继续执行
进程在CPU中运行的过程中
CPU上的各种寄存器,就保存了当前进程运行的“中间状态”
寄存器上关于进程的信息,存放在内存中(PCB自己也会有对应空间)
恢复时:把PCB中刚才保存的属性,填回CPU的寄存器。
- 进程的记账信息
统计功能,统计每个进程在CPU上运行了多久
如果发现某个进程 很久没有吃到CPU资源,就会给它资源倾斜
一点之后,防止这个进程饿死
new(新建),runnable(可运行)、blocked(阻塞)、waiting(等待)
timed_waiting(超时等待) terminated(终止)
三、多线程
进程整体是一个比较“重”的概念
创建进程/销毁进程开销比较大
为了解决上述问题,引入线程,轻量级进程(创建销毁开销小)
- 每个进程都相当于一个要执行的任务
- 每个线程也是一个要执行的任务
进程包含线程
每个进程中,都会包含一个或多个线程进程是操作系统资源分配的基本单位
进程内部所包含的多个线程之间,会共享上述的内存资源和硬盘资源,网络带宽。
进程创建,需要申请资源
进程的销毁,需要释放资源
→ 重量级事物,(代价大,时间消耗)
- 对于线程来说,只是第一个线程创建的时候(和进程一起创建)申请资源,后续再创建线程,不再手动到资源申请操作(内核少,快)
- 只有所有的线程都销毁(进程销毁)才会释放资源,单个进程销毁某个线程,也不会释放资源。
- 可以理解为:在进程创建的时候给线程创建了一次资源,后续线程的创建和销毁都在这一层,只有进程被销毁这一次资源才会被回收。
关于线程的调度,是随机的,我们的程序是感知不到,也干预不了。
那怎么提高效率?
引入多个线程, 确实可以提升效率, 但是资源一旦 就那么多,引入过多线程,会使上下文切换开销增长,因为调度开销会拖累程序的性能。
而且其中一个线程抛出异常,会带走整个进程,所有的线程都无法运行。
如果及时捕获,也不一定 终导致进程终止。
- 创建第一个线程
class MyThread extends Thread {
@Override
public void run() {
System.out.println("hello");
try{
Thread.sleep(1000);
} catch (InterruptedException e) {
// throw new RuntimeException(e); // 处理异常的手段:抛出异常,导致程序异常终止
break;
}
}
}
public class Test {
public static void main(String[] args) {
Thread t = new MyThread();
t.start(); // 开启线程
}
}
其中 Thread.sleep(); 会休眠
例:Thread.sleep(0); 这里是 让此线程放弃当前CPU资源,给别的线程用
在 run() 方法内,Thread.sleep()会抛出异常,但无法 throws,只能使用 try{} catch{} 来捕获。
- 每个线程,调度顺序是随机的,无法预测。“抢占式执行”
创建线程的方法:
继承 Thread,重写 run
实现 Runnable 接口,重写 run
例:Runnable runnable = new MyRunnable();
Thread t = new Thread(runnable);
class MyRunnable implements Runnable { @Override public void run() { System.out.println("我自己的 Runnable 的 run 方法"); } } public class Deano01 { public static void main(String[] args) { Runnable r1 = new MyRunnable(); Thread t1 = new Thread(new MyRunnable()); Thread t2 = new Thread(r1); t1.start(); t2.start(); } }class MyThread extends Thread{ @Override public void run() { System.out.println("我自己的 Thread 的 run 方法"); } } public class Deano01 { public static void main(String[] args) { // 1. 自己实现一个自己的Threa类 Thread t = new MyThread(); t.start(); // 2. 使用 lamabda 表达式 Thread t1 = new Thead(() -> { System.out.println("我自己的 Thread 的 run 方法"); }); } }这个接口中,只有一个无参返回值的 run 方法
可以使用 lambda 表达式 () -> { 要执行的代码 }
在括号里没有参数!
总结
到此这篇关于java中进程详细解析的文章就介绍到这了,更多相关java进程详解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
