程序运行时架构

  • 程序是静态的。
  • 程序运行起来以后,被称作进程。

image.png

操作系统多任务运行环境

钉钉,微信,QQ 音乐,同时可以运行,为什么呢?

计算机的 CPU 核心数是有限的。但是,服务器可以同时处理数以百计甚至数以千计的并发用户请求。

那么,计算机如何做到的?

  • 进程分时执行

进程的运行期状态

运行

运行:当一个进程在 CPU 上进行时,则称该进程处于运行状态。

处于运行状态的进程数目小于等于 CPU 的数目。

就绪

就绪:当一个进程获得了除 CPU 以外的一切所需资源,只要得到 CPU 即可运行,则称此进程处于就绪状态,就绪状态有时候也被称为等待运行状态。

阻塞

阻塞:也称为等待或睡眠状态,当一个进程正在等待某一事件发生(例如等待 I/O 完成,等待锁…)而暂时停止运行,这时即使把 CPU 分配给进程也无法运行,故称该进程处于阻塞状态。

进程 VS 线程

计算机 — 虚拟机进程 — Java 线程

不同进程轮流在 CPU 是哪个执行,每次都要进行进程间 CPU 切换,代价非常大。因此服务器应用通常是单进程多线程。

进程从操作系统获得基本的内存空间,所有的线程共享着进程的内存地址空间

而每个线程也会拥有自己私有的内存地址范围,其他线程不能访问。

image.png

线程栈

  1. void f(){
  2. int x = g(1);
  3. x++;// g 函数返回,当前堆栈顶部为 f 函数栈帧,在当前栈帧继续执行 f 函数的代码
  4. }
  5. int g(int x){
  6. return x + 1;
  7. }

image.png

stackoverflow 栈溢出,栈帧放满了,就会溢出,溢出常见原因:

  • 栈空间开小
  • 递归调用

Java Web 应用多线程运行时视图

image.png

  1. java org.apache.catalina.startup.Bootstrap "$@" start

Tomcat 为每个用户请求分配一个线程。

线程安全

当某些代码修改内存堆(进程共享内存)里的数据的时候,如果有多个线程在同时执行,就可能会出现同时修改数据的情况。

比如,两个线程同时对一个堆中的数据执行 +1
image.png

临界区

多个线程访问共享资源的这段代码被称为 临界区,解决线程安全问题的主要方法是使用锁,将临界区的代码加锁,只有获得锁的线程才能执行临界区代码。

  1. lock.lock();//线程获得锁
  2. i++;//临界区代码,i 位于堆中
  3. lock.unlock();//线程释放锁

阻塞导致高并发系统崩溃

锁(IO)会引起线程阻塞。阻塞导致线程既不能继续执行,也不能释放资源。进而导致资源耗尽。最终导致系统崩溃。

image.png

避免阻塞引起的崩溃

  • 限流:控制进入计算机的请求数,进而减少创建的线程数。
  • 降级:关闭部分功能程序的执行,尽早释放线程。
  • 避免阻塞:异步 IO;无临界区(Actor模型)