1 进程/线程模型
1.1 进程的定义
进程:进程是具有独立功能的程序关于某个数据集合的一次执行过程,也是操作系统进行资源分配和保护的基本单位。
- 程序的一次执行过程,一个程序执行了多次是不同的进程
- 是正在运行程序的抽象,进程是对CPU的抽象
- 将一个物理上的CPU虚拟为多个逻辑上的虚拟CPU,供多个进程同时执行(实际上是交替执行)
- 系统资源以进程为单位分配,如内存、文件等,每个进程具有独立的地址空间
- 操作系统通过调度把cpu的控制权交给某个进程
通过windows下查看任务管理器,或者在linux下执行ps命令来查看有多个进程在运行。
1.2 引入线程机制的动机和思路
操作系统采用进程机制使得多任务能够并发执行,提高了资源使用和系统效率。在早期操作系统中,进程是系统进行资源分配的基本单位,也是处理器调度的基本单位,进程在任一时刻只有一个执行控制流,这种结构称为单线程进程。单线程进程调度时存在进程时空开销大、进程通信代价大、进程并发粒度粗、不适合于并发计算等问题,操作系统引入线程机制来解决这些问题。线程机制的基本思路是,把进程的两项功能——独立分配资源和被调度分派执行分离开来,后一项任务交给线程实体完成。这样,进程作为系统资源分配与保护的独立单位,不需要频繁切换;线程作为系统调度和分派的基本单位会被频繁的调度和切换。
1.3 线程的定义
线程:进程中的一个执行路径。操作系统进程中能够独立执行的实体,是处理器调度和分派的基本单位。线程是进程的组成部分,每个进程包含多个并发执行的线程,同一个进程中的线程共享进程的内存地址和资源,但是不拥有资源。
1.4 线程和进程的区别
(1)定义:进程是程序的一次执行过程,线程是进程的一个执行路径。
(2)角色:在支持线程机制的系统中,进程是系统资源分配的单位,线程是系统调度的单位。
(3)资源共享:进程之间不能共享资源,而线程共享所在进程的地址空间和其它资源。同时线程还有自己的栈,程序计数器等。
(4) 调度:线程是独立调度的基本单位,在同一进程中,线程的切换不会引起进程切换,从一个进程中的线程切换到另一个进程中的线程时,会引起进程切换。
(5)独立性:进程有自己独立的地址空间,而线程没有,线程必须依赖于进程而存在。
(6)系统开销:由于创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、I/O 设备等,所付出的开销远大于创建或撤销线程时的开销。类似地,在进行进程切换时,涉及当前执行进程 CPU 环境的保存及新调度进程 CPU 环境的设置,而线程切换时只需保存和设置少量寄存器内容,开销很小。
(7)通信方面:线程间可以通过直接读写同一进程中的数据进行通信,但是进程通信需要借助 IPC。
2 进程状态切换
进程的三种基本状态:运行态、就绪态、等待态
- 运行态(Running):占有CPU,并在CPU上运行
- 就绪态(Ready):已经具备运行条件,但由于没有空闲CPU,而暂时不能运行
- 等待态(Waiting/Blocked):等待资源
需要注意:
- 只有就绪态和运行态可以相互转换,其它的都是单向转换。就绪状态的进程通过调度算法从而获得 CPU 时间,转为运行状态;而运行状态的进程,在分配给它的 CPU 时间片用完之后就会转为就绪状态,等待下一次调度。
- 阻塞状态是缺少需要的资源从而由运行状态转换而来,但是该资源不包括 CPU 时间,缺少 CPU 时间会从运行态转换为就绪态。
2.1 java中线程的状态及状态转换
Java语言中定义了5中线程状态,在任意一个时刻,一个线程只能有且只有一种状态,这五种状态分别如下:
新建(New):创建后尚未启动的线程处于这种状态
运行(Runnable):Runnable包括了操作系统线程状态中的Running和Ready,也就是处于此状态的线程可有可能正在执行,也有可能正在等待CPU为它分配执行时间。
无限期等待(Waiting):处于这种状态的线程不会被分配CPU执行时间,它们要等待被其他线程显式地唤醒。以下方法会让线程陷入无限期的等待状态:
- 没有设置Timeout参数的Object.wait()方法
- 没有设置Timeout参数的Thread.join()方法
- LockSupport.park()方法
期限等待(Timed Waiting):处于这种状态的线程也不会被分配CPU执行时间,不过无须等待被其他线程显式唤醒,在一定时间之后他们会由系统自动唤醒。一下方法会让线程进入期限等待状态:
- Thread.sleep()方法
- 设置了Timeout参数的Object.wait()方法
- 设置了Timeout参数的Thread.join()方法
- LockSupport.parkNanos()方法
- LockSupport.parkUntil()方法
阻塞(Blocked):线程被阻塞,“阻塞状态”与“等待状态”的区别是:阻塞状态在等待着获取一个排它锁,这个事件将在另一个线程放弃这个锁的时候发生;等待状态则是在等待一段时间或者唤醒动作的发生。在线程等待进入同步区域的时候,线程将进入这种状态。
结束(Terminated):已终止线程的线程状态,线程已经结束执行。
3 处理器调度
CPU调度:控制、协调进程对CPU的竞争,即即按一定的调度算法从就绪队列中选择一个进程,把CPU的使用权交给被选中的进程;如果没有就绪进程,系统会安排一个系统空闲进程或idle进程。
调度算法总体上可以分为两类:
-
抢占式调度:抢占式调度指的是每条线程执行的时间、线程的切换都由系统控制,线程的切换不由线程本身决定,系统控制指的是在系统某种运行机制下,可能每条线程都分同样的执行时间片,也可能是某些线程执行的时间片较长,甚至某些线程得不到执行的时间片。在这种机制下,一个线程的堵塞不会导致整个进程堵塞。
-
非抢占式调度(协同调度):协同式调度指某一线程执行完后主动通知系统切换到另一线程上执行,线程的执行时间由线程本身控制,这种模式就像接力赛一样,一个人跑完自己的路程就把接力棒交接给下一个人,下个人继续往下跑。线程的执行时间由线程本身控制,线程切换可以预知,不存在多线程同步问题,但它有一个致命弱点:如果一个线程编写有问题,运行到一半就一直堵塞,那么可能导致整个系统崩溃。
不同环境的调度算法目标不同,因此需要针对不同环境来讨论调度算法。
3.1 批处理系统中的调度算法
批处理系统没有太多的用户操作,在该系统中,调度算法目标是保证吞吐量和周转时间(从提交到终止的时间)。
- 先来先服务(FCFS-First Come First Serve)
非抢占调度,按照进程就绪的先后顺序使用CPU。优点是公平实现简单,缺点是有利于长作业,但不利于短作业,因为短作业必须一直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成了短作业等待时间过长。 - 短作业优先(SJF-Shortest Job First)
非抢占式调度,具有最短完成时间的进程优先执行。优点是具有最短的平均周转时间,但是不公平,如果一直有短作业到来,那么长作业永远得不到调度,产生 “饥饿”现象 (starvation)。 - 最短剩余时间优先(SRTN-Shortest Remaining Time Next)
SJF抢占式版本,即当一个新就绪的进程比当前运行进程具有更短的完成时间时,系统抢占当前进程,选择新就绪的进程执行。 - 最高响应比优先(HRRN-Highest Response Ratio Next)
是一个综合的算法调度时,首先计算每个进程的响应比R;之后,总是选择 R 最高的进程执行。其中响应比R= 周转时间 / 处理时间 =(处理时间 + 等待时间)/ 处理时间 = 1 +(等待时间 / 处理时间)。如果是短作业由于处理时间少很快就会上CPU执行,如果是长作业等待的时间越长,上CPU执行的机会就越大,从而避免长作业的饥饿。