7.4 操作系统:计算机如何处理成百上千的并发请求?
7.4 操作系统:计算机如何处理成百上千的并发请求?
1.程序运行时架构
程序是静态的。
程序运行起来后,被称作进程。
2.操作系统多任务运行环境
计算机的核心数是有限的。但是,服务器可以同时处理数以百计甚至数以千计的并发用户请求。
那么,计算机如何做到的?
进程分时执行。
3.进程的运行期状态
运行:进程在 CPU 上运行,则称进程处于运行态。处于运行状态的进程数目<=CPU 数目。
就绪:当一个进程获得了一切所有资源,除 CPU 外。只要得到 CPU 即可运行,
则称此进程处于就绪状态,就绪状态有时候也被成为等待运行状态。
阻塞:也成为等待或者睡眠状态,当一个进程正在等待某一事件发生(例如:等待 I/O 完成,等待锁...)
而暂时停止运行,这时即使把 CPU 分配给进程,进程也无法运行,故称为进程处于阻塞状态。
4.进程 VS 线程
不同进程轮流在 CPU 上执行,每次都要进程间切换 CPU,代价非常大=>服务器应用通常是单进程多线程。
进程向操作系统申请内存空间,所有的线程共享着进程的内存地址空间。
而每个线程也会拥有自己私有的内存地址范围,其他线程不能访问。
5.线程栈
多个线程,执行相同的代码,却得到不同的结果,怎么做到的?
void f(){
int x=g(1);
x++;//g 函数返回,当前堆栈顶部为 f 函数栈帧,在当前栈帧继续执行 f 函数的代码
}
int g(int x){ return x+1;}
6.Java Web 应用多线程运行时视图
启动 JVM 命令: java org.apache.catalina.startup.Bootstrap "$@" start --找到入口方法:main
物理服务器(安装 OS)=>操作系统=>OS 启动 JVM 进程=>JVM 启动 Web 容器=>运行应用程序 War 包
1.进程和线程来源:Tomcat(startup.sh:java org.apache.catalina.startup.Bootstrap "$@" start)
1.1JVM 进程启动: java 启动 java 虚拟机(JVM)。 操作系统 OS 解析"java"命令,启动 JVM。
JVM 启动后,加载入口类---Tomcat 提供的 Bootstrap 的 main 方法。
JVM 从入口函数 main 方法开始执行。==>JVM 进程启动
1.2Tomcat 启动多线程:启动 80 端口,监听 HTTP 请求;启动多个线程,等待端口连接。
监听 HTTP 请求到达 80 端口后,Tomcat 分派一个线程处理该端口连接。
Tomcat 解析 HTTP 请求数据成 Request 对象,Response 对象,Session 上下文等。主类初始化完成。
主类初始化完成,调用应用程序的 Servlet 代码。
==>此时,Tomcat 已经启动了多个线程。每个请求进来,Tomcat 就会分配一个线程处理该用户请求。
调用 doGet,doPost 等方法。
1.3 多线程并行执行:
=>并发请求进来,Tomcat 分配多个线程同时处理请求,多个请求并发调用 doGet,doPost.
=>多个线程如果要使用锁,就要在锁上等待。通常不会再 Servlet 里面加锁操作--避免资源竞争,避免阻塞。
1.4Spring 框架:调用 spring 的 servlet,servlet 调用 controller 类。
7.线程安全
当某些代码修改内存堆(进程共享内存)里的数据的时候,如果有多个线程同时执行,就可能出现同时修改数据的情况。
比如:++i. 两个线程同时对一个堆中的数据执行+1 操作,得到结果 1,但是期望结果 2.
++i 符合操作(读取-操作-保存)
多个线程同时修改 Student 对象,数据正确性无法保证。如何保证数据安全?线程安全?
8.临界区
多个线程访问共享资源的这段代码被称为临界区。
解决线程安全问题的主要方法是使用锁,将临界区的代码加锁,只有获得锁的线程才能执行临界区代码。
lock.lock();//线程获得锁,创建临界区
i++;//临界区代码,i 位于堆中。
lock.unlock();//线程释放锁
9.阻塞导致高并发系统崩溃
锁(I/O)会引起线程阻塞。阻塞导致线程既不能继续执行,也不能释放资源。进而导致资源耗尽。最终导致系统崩溃。
10.避免阻塞引起的崩溃
限流:控制进入计算机的请求数,进而减少创建的线程数。架构层面控制。--某种程度的不可用。
降级:关闭部分功能程序的执行,尽早释放线程。
反应式:异步;无临界区(Actor 模型)
评论