程序运行时架构
- 程序是静态的。
- 程序运行起来以后,被称作进程。
操作系统多任务运行环境
钉钉,微信,QQ 音乐,同时可以运行,为什么呢?
计算机的 CPU 核心数是有限的。但是,服务器可以同时处理数以百计甚至数以千计的并发用户请求。
那么,计算机如何做到的?
- 进程分时执行。
进程的运行期状态
运行
运行:当一个进程在 CPU 上进行时,则称该进程处于运行状态。
处于运行状态的进程数目小于等于 CPU 的数目。
就绪
就绪:当一个进程获得了除 CPU 以外的一切所需资源,只要得到 CPU 即可运行,则称此进程处于就绪状态,就绪状态有时候也被称为等待运行状态。
阻塞
阻塞:也称为等待或睡眠状态,当一个进程正在等待某一事件发生(例如等待 I/O 完成,等待锁…)而暂时停止运行,这时即使把 CPU 分配给进程也无法运行,故称该进程处于阻塞状态。
进程 VS 线程
计算机 — 虚拟机进程 — Java 线程
不同进程轮流在 CPU 是哪个执行,每次都要进行进程间 CPU 切换,代价非常大。因此服务器应用通常是单进程多线程。
进程从操作系统获得基本的内存空间,所有的线程共享着进程的内存地址空间。
而每个线程也会拥有自己私有的内存地址范围,其他线程不能访问。
线程栈
void f(){
int x = g(1);
x++;// g 函数返回,当前堆栈顶部为 f 函数栈帧,在当前栈帧继续执行 f 函数的代码
}
int g(int x){
return x + 1;
}
stackoverflow 栈溢出,栈帧放满了,就会溢出,溢出常见原因:
- 栈空间开小
- 递归调用
Java Web 应用多线程运行时视图
java org.apache.catalina.startup.Bootstrap "$@" start
Tomcat 为每个用户请求分配一个线程。
线程安全
当某些代码修改内存堆(进程共享内存)里的数据的时候,如果有多个线程在同时执行,就可能会出现同时修改数据的情况。
比如,两个线程同时对一个堆中的数据执行 +1
临界区
多个线程访问共享资源的这段代码被称为 临界区,解决线程安全问题的主要方法是使用锁,将临界区的代码加锁,只有获得锁的线程才能执行临界区代码。
lock.lock();//线程获得锁
i++;//临界区代码,i 位于堆中
lock.unlock();//线程释放锁
阻塞导致高并发系统崩溃
锁(IO)会引起线程阻塞。阻塞导致线程既不能继续执行,也不能释放资源。进而导致资源耗尽。最终导致系统崩溃。
避免阻塞引起的崩溃
- 限流:控制进入计算机的请求数,进而减少创建的线程数。
- 降级:关闭部分功能程序的执行,尽早释放线程。
- 避免阻塞:异步 IO;无临界区(Actor模型)