一、进程是什么
进程是操作系统分配资源的基本单位,每一个进程可以看做一个工厂,这个工厂利用操作系统分配给它的资源(内存空间、CPU等)来执行操作系统的指令。
二、进程是怎么管理的
操作系统通过描述和组织来进行进程的管理。
描述:要描述一个进程,就要明确进程中的一些基本属性,操作系统内部主要通过C / C++来实现的,这里的描述使用的其实就是C语言中的结构体。描述进程的结构体就叫做PCB(进程控制模块)在Linux操作系统中,PCB实际上是与线程一 一绑定的,一个进程对应的通常是一组PCB。
PCB基础属性:
pid:类似省份正的id,用作进程的身份标识。操作系统中通常有很多进程在运行,为了将数据传输给对应的进程,就需要这样的身份标识。
内存指针:进程的运行,其实就是执行它对应的虚拟地址空间中的代码,内存指针就是指明这个进程需要执行的代码的位置,以及执行这段代码所需的数据的位置。
文件描述符表:程序的运行时常要与文件打交道,进程每打开一个文件,就在文件描述符表中加一项。文件描述符表可以理解成一个数组,里面的每一项相当于一个结构体,对应文件的相关信息。进程开始运行后,会默认打开三个文件:标准输入(System.in),标准输出(System.out)和标准错误(System.err),文件描述符表的下标就叫文件描述符。
PCB实施调度的属性(多个进程同时运行时,操作系统通过时间规划,调整各个进程的执行情况,进程的调度就是操作系统在考虑如何给进程分配资源):
状态:决定进程接下来如何调度。就绪状态:随时可以在CPU上执行。阻塞/休眠状态:暂时不能在CPU上执行。
优先级:描述进程能分配的资源多少以及分配的先后顺序。
记账信息:描述进程执行了多久,执行了哪些指令以及排队排了多久,给进程的调度提供依据,平衡优先级造成的资源分配不均。
上下文:类似存档,记录进程被调出CPU时的状态,使下次进程被调进CPU时能以相同的状态继续执行。进程在被调出CPU时,会将进程在CPU寄存器中的数据保存到内存中。
三、进程的调度
进程的调度其实就是操作系统的时间管理,调度进程就是安排进程在什么时候进入CPU执行,什么时候离开CPU。进程被如何调度以及什么时候调度取决于进程当前的状态以及进程的优先级。
四、进程的独立性是怎么回事,进程之间是怎么通信的
独立性:进程是在操作系统分配的虚拟内存地址中执行指令的,所以进程之间是独立开的。一个进程挂掉,一般不会影响其他进程的运行。
进程间通信:进程之间的通信是通过一个公用空间进行的,一个进程将数据放入这个空间,另一个进程要读取数据时,就在这个空间里读取,进程之间仍旧是彼此独立。这些公共空间也有很多种,有些速度快,有些速度慢,有的空间大,有的空间小,目前最主要的两种进程间通信方式是:文件操作、网络操作(socket)。
五、线程是什么,进程和线程的关系以及区别
线程是操作系统执行调度的基本单位。每一个线程可以执行一个任务(一段代码)。
进程和线程是包含关系,进程包含线程。我们可以用工厂和生产线来理解进程和线程。
为了提高生产效率,我们有两种方法:
1.增设工厂:增设工厂,多个工厂同时工作,工作效率就提高了,这就相当于创建多个进程,多个进程同时执行,执行速度自然比一个进程单独执行要快。
2. 在工厂内部增加生产线:多条生产线当然要比一条生产线工作效率高,这就相当于创建多条线程。
区别:
1.二者消耗的资源量不同。创建工厂当然要比在工厂中增加生产线的成本高,创建进程和线程也是一样的。频繁的创建、销毁和调度进程,成本是比较高的,这里的成本包括时间和CPU资源等等,对这些资源的申请和释放都很低效。而频繁的操作线程,成本要低,速度要更快,这是因为同一个进程中的多个线程,使用的都是这一个进程分配到的空间和文件,频繁的创建和销毁线程,不需要重复申请和释放资源。
2.进程之间具有独立性,一个进程挂掉一般不会影响别的进程工作,就像一个工厂出问题了,另外的工厂一般不会受到影响一样,而同一个进程中的线程是不具备这种独立性的,就像同一个工厂中的众多生产线,一条生产线出问题,一般都会影响到别的生产线,这是因为同一个进程中的这些线程所使用的,都是这一个进程分配到的虚拟地址空间。
六、并发和并行
我们创建多个进程、多个线程都是为了提高代码的执行效率,而提高代码效率就是通过并发和并行。
并发:在微观上,并发是指单个CPU一会执行指令A,一会执行指令B,一会执行指令C,然后再回头执行指令A,如此循环。当然,这只是一个例子,实际上CPU执行指令的顺序并不是这样的,而是随机的,是抢占式执行。这样切换执行指令,如果切换的够快,在宏观上看就和同时执行多个指令一样,这就是并发执行。
并行:并行是多个CPU同时执行不同的指令,CPU_A执行指令A,CPU_B执行指令B,是真正的同时执行。
七、线程的创建
在Java中是通过Thread类来创建线程的,Thread是一个抽象类,不能直接实例化对象。创建Thread对象后,操作系统内还不会生成线程,需要通过这个实例化对象调用start( )方法,start( )方法执行才会在操作系统中生成线程。创建线程的Java代码有以下几种写法。
public class demo1 {
public static void main(String[] args) {
//创建线程,在Thread()中传入MyThread1实例。
Thread t1 = new Thread(new MyThread1());
t1.start();
}
}
//创建一个类,实现Runnable接口。
class MyThread1 implements Runnable {
//重写run方法,run方法中写入线程需要执行的代码。
@Override
public void run() {
System.out.println("线程1");
}
}
public class demo2 {
public static void main(String[] args) {
//抽象类不能直接实例化对象,通过子类来实例化对象。
Thread t2 = new MyThread2();
t2.start();
}
}
//创建一个类,继承Thread
class MyThread2 extends Thread {
//重写run方法,写入线程需要执行的代码。
@Override
public void run() {
System.out.println("线程2");
}
}
public class demo3 {
public static void main(String[] args) {
//匿名内部类写法,实现继承自Thread的匿名内部类,并重写run方法
Thread t3 = new Thread() {
@Override
public void run() {
System.out.println("匿名内部类继承自Thread");
}
};
t3.start();
//匿名内部类继承Runnable接口,new出的实例传给Thread
Thread t4 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("实现Runnable接口的匿名内部类");
}
});
t4.start();
//匿名内部类的lambda表达式写法,相当于Runnable写法。
Thread t_4 = new Thread(() -> {
System.out.println("线程4");
});
t_4.start();
}
}