为了写出并发性能更加“隽永”的代码,我们需要厘清并组织好任务的逻辑关系:
首先我们将任务抽象成 离散的工作单元
然后将多个任务组织成程序的结构
再根据自然事务(其实就是业务需求)的边界情况来处理错误(超时等等)恢复过程
最后在一些地方使用并行的计算来提升并发性
(总之,本章就是阐述 任务执行框架&任务设计思想)
从任务设计的角度思考(比如说服务器无法预料到请求到达的情况),因为自然会想将任务设计得相互独立,这样可以获得更好的调度、更容易负载均衡,最终实现高吞吐、快速响应的美好愿望。因此,这段废话的意思就是说 我们得从思考并得到清晰的 任务边界(说破了就是线程的最小粒度,但这里我们先配合一波作者) 出发来解决问题。
以web服务器应用为例,这些应用大多都会自然而然的选择一种任务边界——以独立的客户请求为边界。
于是,我们开始了拿手的极端思考方式:
1.1 串行地执行任务(就一条线程梭哈到底)存在一些问题:
网络波动(程序难以控制的)、SocketIO(将带来阻塞)、本身就不能避免的计算(还不排除这是个大运算的情况)、阻塞后面来的请求。
这样串行的执行很容易带来 低吞吐、灵敏性拉跨的响应、服务器资源利用率感人(CPU在IO阻塞期间将空闲)
在某些情况下,串行处理方式能带来简单性或安全性(GUI就这么搞的)
1.2 显式地为任务创建线程(为每个任务分配一条线程)主线程不断地交替指向"接受外部连接"(SocketIO)、“分发请求”(dispatch),然后循环创建新线程来处理请求。
可以得到一下推论:
将任务处理从主线程中剥离 => 减少了对后面来的请求的阻塞
并发地处理任务 => 更好的CPU利用率
任务处理的代码会被并发调用 => 必须保证其线程安全性
这种方法可以替代串行执行,并且提升性能的前提是:请求到达速率不超过服务器对请求的处理能力。
1.3 无限制创建线程的不足前者存在的缺陷:
线程生命周期的开销非常高,因为线程的创建过程需要JVM、操作系统(这将导致不同平台的开销不同)提供一些辅助操作,大多服务器应用的请求处理的开销 较 线程创建而言,都是轻量级的。
资源消耗,活跃的线程会消耗系统资源,尤其是内存。如果可运行的线程数量多于可用处理器的数量,那么有些线程将闲置,这些闲置的线程会占据许多内存,也给后面的垃圾回收器带来压力,而且大量的线程还会增加CPU资源的竞争(同时会带来其他的开销)
稳定新,在可创建线程的数量上存在一个限制,这个限制值随平台的不同而不同,并且受到多个因素的制约:JVM启动参数、Thread构造函数中请求的栈大小以及底层操作系统对线程的限制。破坏这些限制将导致OOM,并且要像这种错误中恢复过来是非常危险的,更简单的办法是避免超出这些限制。
在一定范围内,增加线程可以提供系统的吞吐率,超出这个范围,再创建线程只会降低程序的执行速度,甚至导致应用程序崩溃。
如果服务器需要提供高可用性,并且在高负载情况下能平缓地降低性能,那么过多的创建线程将成为严重的故障。



