1.进程与线程
进程: 系统进行资源调度和分配
的最小独立单位。具有独立性,动态性,并发性,异步性。实现了os的并发性
线程: 线程是进程的实体,是CPU调度和分派的基本单位。拥有一些运行中必不可少的资源,如程序计数器、寄存器和栈等。
联系:
- 进程(主线程)创建了多个线程,各个子线程拥有自己的独立栈空间(存储函数参数、局部变量等),多个子线程与主线程共享堆、全局变量等非栈内存。
- 一个程序至少有一个进程,一个进程至少有一个线程,线程依赖于进程而存在
一个线程挂掉,会导致该线程所属的进程整个挂掉,进程中的其他线程也都挂掉,但是一个进程挂掉,不会影响其他进程
2.进程间的通信
2.1管道pipe
2.2消息队列
2.3信号量
2.4共享内存
2.5套接字
3.线程间的通信
python中实现线程:threading模块
3.1全局变量
3.2 消息队列(threading模块中的Queue类)
4.常见锁
4.1 互斥锁(同步机制)
4.2 信号量
一次允许多个线程操作锁对象
4.3 读写锁
rwlock
区分读和写,处于读操作时,可以允许多个线程同时或得读操作,但是同一时刻只能有一个线程获得写锁
- 写锁会阻塞其他读写锁;当一个线程获得写锁进行写操作,读锁也不能被其他线程获得
- 写优先于读,当有线程因为等待写锁而进入睡眠时,后续读者必须等待
场景:读取数据的速率远远大于写数据的频率
5. 死锁产生的原因及解决策略
5.1什么是死锁
在两个或者多个并发进程中,如果每个进程持有某种资源而又等待其他进程释放它们现在保持着的资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁。即:两个或多个进程无限期的阻塞、相互等待的状态
5.2 产生死锁的原因
-
竞争资源
-
进程间推进顺序不当
5.3 四个必要条件:
- 互斥条件:一个资源一次只能被一个进程使用
- 请求和保持条件:一个进程因请求资源而阻塞时,对已获得资源保持不放
- 不剥夺条件: 进程已获得的资源在未使用完之前不能被剥夺,只能使用完后自己释放
- 循环等待条件: 在发生死锁时,若干进程之间形成一个进程-资源头尾相接的环形等待资源关系
5.4 预防死锁
- 资源一次性分配:一次性分配所有资源,(破坏请求条件)
- 只要有一个资源得不到分配,也不给这个进程分配其他的资源(破坏请保持条件)
- 可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源(破坏不可剥夺条件)
- 资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)
参考博客
5.5 避免死锁
- 在避免死锁的策略中,允许进程动态地申请资源。因而,系统在进行资源分配之前预先计算资源分配的安全性。若此次分配不会导致系统进入不安全的状态,则将资源分配给进程;否则,进程等待。其中最具有代表性的避免死锁算法是
银行家算法
银行家算法
5.6 检测死锁
- 首先为每个进程和每个资源指定一个唯一的号码
- 然后建立资源分配表和进程等待表
- Jstack命令
jstack是java虚拟机自带的一种堆栈跟踪工具 - JConsole工具
Jconsole是JDK自带的监控工具,在JDK/bin目录下可以找到。它用于连接正在运行的本地或者远程的JVM,对运行在Java应用程序的资源消耗和性能进行监控,并画出大量的图表,提供强大的可视化界面
6.进程调度算法
- 先来先服务
- 短作业优先
- 高响应比优先
- 优先级调度算法
- 多级反馈队列
7. 内存算法
- FIFO先进先出
思想:先进入的页面先淘汰,采用队列实现,先进先出 - OPT最佳算法
思想:淘汰以后不需要使用或者很久才会用到的页面 - LRU最久未使用淘汰算法
思想:淘汰最长时间没有被使用的页面,可以使用双向链表实现,访问的时候将该页移至头部,淘汰时淘汰尾部 - LFU最不常使用淘汰算法
思想:淘汰访问频率最小的页,以次数为参考。新加入的页放在末尾,计数器置1,每次访问计数器加1,并刷新计数器大小排序,淘汰计数最小的页
8. 堆和栈的区别
- 堆(内存):由开发人员分配,存储
数组、对象
等;类似于栈 - 栈(内存):由操作系统自动分配释放,存储
局部变量、函数的参数值
等;类似于链表 - 栈使用一级缓存,通常都是被调用时处于存储空间,调用完毕立即释放
- 堆使用二级缓存,生命周期由虚拟机的垃圾回收机制决定
9.内存泄露和内存溢出
9.1内存泄露
程序在申请内存后,无法释放已申请的内存空间而造成内存泄露;多次内存泄露堆积后造成内存溢出
9.2内存溢出
程序申请内存后,没有足够的内存提供申请使用,即内存不够
9.3 内存泄露与内存溢出的关系
- 内存泄露的堆积最终导致内存溢出
- 内存溢出就是所需的内存空间超过了系统实际分配的空间
- 内存泄露是指向系统申请分配内存进行使用(new),可是使用完以后却不归还,导致申请到的那块内存自己也不能再访问,系统也无法重新分配
9.4 内存泄露的分类
-
常发性内存泄露:发生内存泄漏的代码会被多次执行到
-
偶发性内存泄露:发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的
-
一次性内存泄露::发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏
-
隐式内存泄露:程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存
9.5 解决方法
排查对象:
- 检查数据库查询:采用分页查询方式
- 代码中是否有死循环或递归
- 是否有大循环重复产生新对象实体
- 检查对数据库查询中,是否有一次获得全部数据的查询
- 检查List、MAP等集合对象是否有使用完后,未清除的问题
内存溢出:
1、修改JVM启动参数,直接增加内存。(-Xms,-Xmx参数一定不要忘记加。)
2、检查错误日志,查看“OutOfMemory”错误前是否有其 它异常或错误
3、对代码进行走查和分析,找出可能发生内存溢出的位置。
10.多线程和多进程,及应用场景
1.多进程应用场景:
- 几乎所有的web server服务器服务都有多进程
- chrome浏览器也是多进程方式
- redis
2.多线程应用场景
- 线程间数据共享(不同任务间需要大量共享数据或频繁通信时
- 提供非均质的服务(有优先级任务处理)事件响应有优先级
- 单任务并行计算,在非CPU Bound的场景下提高响应速度,降低时延
- 与人有IO交互的应用,良好的用户体验(键盘鼠标的输入,立刻响应
3.如何选择多线程or多进程?
- ①需要频繁创建销毁的优先用线程(进程的创建和销毁开销过大)
- ②需要进行大量计算的优先使用线程(CPU频繁切换)
- ③强相关的处理用线程,弱相关的处理用进程
- ④可能要扩展到多机分布的用进程,多核分布的用线程
- 实际开发:“进程+线程”结合方式
11. 多线程如何实现
多进程
多线程
- 普通创建方式
import threading
def run():
......
t1 = threading.Thread(target=run, args=("t1",))
- 继承threading.Thread
重构run方法
import threading
class MyThread(threading.Thread):
def __init__(self, n):
super(MyThread, self).__init__() # 重构run函数必须要写
self.n = n
sleep的时候是不会占用cpu的,在sleep的时候操作系统会把线程暂时挂起
12. 计算机内存管理方式
13.Linux用户态和内核态
Linux云计算网络
用户态:提供应用程序运行的空间,
内核态:特殊的软件程序,控制计算机的硬件资源,例如协调CPU资源,分配内存资源,并且提供稳定的环境供应用程序运行
zhihu
14. 进程的状态
-
就绪态Ready:进程已获得除处理器外的所需资源,只是在等待分配处理器资源,只要分配了处理器进程就可执行(准备就绪进程可以按多个优先级来划分队列)
-
运行态Running:进程占用处理器资源,处于此状态的进程的数目小于等于处理器的数目。在没有其他进程可以执行时(如所有进程都在阻塞状态),系统通常会自动执行系统的空闲进程。
-
阻塞态Blocked:系统由于进程等待某种条件(如I/O操作或进程同步),在条件满足之前无法继续执行。该事件发生前即使把处理器资源分配给该进程,该进程也无法进行运行
15.虚拟内存的作用
虚拟内存:把外存当作内存使用,即用硬盘来模拟内存使用,可以缓解物理内存不足的压力,有利于提升资源使用率
虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。目前,大多数操作系统都使用了虚拟内存,如Windows家族的“虚拟内存”;Linux的“交换空间”等
详细链接
16.线程池Pooling
17.线程安全的实现
18.进程和线程的切换
进程的切换
- 切换页目录,以使用新的地址空间
- 切换内核栈及硬件上下文
线程的切换:
切换内核栈及硬件上下文
博客
19.协程
协程是轻量级线程,协程运行在线程之上,当一个协程执行完成后,可以选择主动让出,让另一个协程运行在当前线程之上。协程并没有增加线程数量,只是在线程的基础之上通过分时复用的方式运行多个协程,而且协程的切换在用户态完成,切换的代价比线程从用户态到内核态的代价小很多。
知乎
20.线程同步的方式
1)互斥量
采用互斥对象机制,只有拥有哦互斥对象的线程才能访问公共资源
2)信号量
它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量
3)信号
通过通知操作的方式来保持线程同步,还可以方便实现多线程优先级的比较操作
临界区:
通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。在任意时刻只允许一个线程对共享资源进行访问,如果有多个线程试图访问公共资源,那么在有一个线程进入后,其他试图访问公共资源的线程将被挂起,并一直等到进入临界区的线程离开,临界区在被释放后,其他线程才可以抢占。它并不是核心对象,不是属于操作系统维护的,而是属于进程维护的
参考博客