进程与线程
最后一次更新时间:Sunday, November 8th 2020, AM
进程间通信
匿名管道:
用于具有亲缘关系(父子、兄弟)的进程之间通信。有名管道:
FIFO,任意两个进程。信号:
用于通知某个进程发生了某事件。消息队列:
FIFO,也可以随即查询,更灵活。
克服了信号信息承载量少的缺点。
克服了管道只支持字节流,缓冲区大小受限的缺点。信号量:
是一个计数器,用于多进程访问共享数据,帮助进程间同步。共享内存:
依赖于同步操作,最有用的进程间通信方式。套接字:
Client与Server的进程之间进行通信
线程间同步
互斥量(MuteX):
采用互斥对象机制(把对象加上互斥锁),拥有互斥对象的线程才能访问。例如Java的Synchronized关键字及其各种锁。信号量:
允许同一时刻,多个线程访问统一资源。但需控制最大线程数量。事件(Event):
wait() / notify():通过通知操作,来保持多线程同步,还能实现优先级比较。
进程的调度算法
先到先服务(FCFS):
顾名思义,不解释。短作业优先(SJF):
顾名思义,不解释。时间片轮转:
最古老、最公平、最简单、使用最广。多级反馈队列:
高优先级 && 短作业 的进程优先处理。公认效果较好,Unix用的就是这种调度算法。优先级调度:
顾名思义,不解释。
OS内存管理
OS内存管理的内容
分配、回收、逻辑地址与物理地址的转换。
OS内存管理的机制
连续分配
- 块式管理:
非常古老,把内存分为几个固定大小的块,每个块只存放一个进程。
- 块式管理:
非连续分配
页式管理:
把内存划分为一页一页,页较小,比块式分配粒度更大,减少了碎片。通过页表中的映射,来管理逻辑与物理地址的转换。段式管理:
粒度更小,进一步减少了碎片。每个段都定义了信息。段页式管理:
内存先分成段,再分成页,并提高了运行时的安全性。
分页与分段
相同处:提高内存利用率,减少了碎片。离散存储,但每个段/页都是连续的。
不同处:页的大小是固定的,段的大小取决于程序而变化。分页满足OS的性能需求,分段满足用户的动态需求。
CPU寻址(虚拟寻址)
CPU将虚拟地址翻译为物理地址。这个过程需要CPU中的内存管理单元。
若无虚拟地址的存在(早期OS)的影响:程序可访问任意内存,不安全。且容易造成数据覆盖,很难同时运行多个程序。
虚拟内存
可以让程序拥有超出物理内存实际大小的空间,且为每个进程提供一致的、私有的空间。管理高效,减少出错。
局部性原理
- 时间局部性:执行了一条指令,通常不久后会再次执行。
- 空间局部性:访问了一个数据,通常不久后会再次访问。
并发与并行
- 并发:同一时间段,多任务执行。
- 并行;同一时间点,多任务执行。
并行是并发的一个子集,着重于“同时”。
线程的生命周期
上下文切换
一个线程的时间片用完,回到 READY就绪态
把CPU让给其它线程使用
切换之前,先保存一下自己的状态
切换回来再加载这个状态
上下文切换可能是OS中最耗时的操作
死锁
产生死锁需要满足四个条件
- 互斥:该资源一次只能被一个线程占用
- 请求和保持:阻塞时,不释放现有资源
- 不剥夺:资源不能被剥夺,只能自己用完释放
- 形成循环:若干线程形成循环等待
死锁的原因
- 资源不足
- 资源分配不合理
- 进程顺序不合理
sleep() 和 wait()
- 都可以暂停线程
- sleep()不释放锁,wait()释放
- wait()常用于线程间通信,sleep()只是暂停线程
- wait()需要notify()唤醒,sleep()超时自动唤醒
调用start()执行run() 与 直接调用run()
start()是把线程转换为RUNNABLE状态,CPU自动运行,是真正的多线程。
run()只是一个普通调用,顺序执行的一个普通方法。
ThreadLocal 线程局部变量
创建一个ThreadLocal变量,每个访问该变量的线程都会保存它的本地副本。
get()获取其默认值,set()修改其本地副本的值。
创建线程的四个方法
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
- 从线程池中获取
终止线程
- 设置一个flag(退出标志),运行完自行终止。
- 使用interrupt()抛出异常,终止线程。
- 使用stop()强行终止。(不安全,已废弃,不推荐)
如何保证线程安全
- 对非安全的代码加锁
- 使用线程安全的类
- 多线程并发时,把线程共享的变量,改为方法级的局部变量
守护线程(Daemon Thread)
又名 服务/精灵/后台 线程
用来保持JVM运行,优先级较低。
线程池
线程池的作用(优点)
- 限制线程的数量,不会因线程过多导致系统性能下降。
- 不需要频繁创建/销毁,节省系统开销。
- 可以统一进行管理。
线程池的组成
- 管理器:创建、管理线程池。
- 工作线程:池中的线程,无任务时处于等待态,循环使用。
- 任务接口:每个任务必须实现的接口,以供工作线程调度。其规定了任务入口、任务完成后的收尾工作,任务的状态。
- 任务队列:存放未处理的任务(缓冲区)
线程池的分类
- newFixedThreadPool:指定工作线程的数量。
- newCachedThreadPool:工作线程数量可变,空闲时(默认1分钟)则终止一个线程。
- newSingleThreadExecutor:只有一个工作线程,若意外终止,会自动重新创建。
- newSchduleThreadPool:工作线程数量固定,且支持周期性任务。
除特别声明外,本站所有文章均采用 CC BY-SA 4.0 协议 ,转载请注明出处!