本系列译自jakob jenkov的Java并发多线程教程,个人觉得很有收获。由于个人水平有限,不对之处还望矫正!
尽管多线程有诸多的挑战,但是多线程被广泛使用的原因有以下几点:
1、对资源的充分利用。
2、简化程序设计
3、响应的及时性
资源的充分利用
假设一个应用程序从本地文件系统中读取并处理一个文件,让我们来假设从硬盘读取文件需要5秒,处理文件需要两秒,那么处理两个文件则需要:
5秒 读取A文件
2秒 处理A文件
5秒 读取B文件
2秒 处理B文件
共需14秒
当从硬盘读取文件的时候,CPU大部分时间用在等待从硬盘读取数据,CPU 在等待的这段时间是非常空闲的,是可以被利用来做其他事情的,通过改变执行顺序,CPU 可以被更好的利用。看下面的执行顺序:
5秒 读取A文件
5秒 读取B文件+2秒处理A文件
2秒 处理B文件
共需12秒
到CPU等待A文件读取的时候,开始读取B文件,当B文件正在读取的时候,CPU 开始处理A文件,记住,当CPU 等待从硬盘读取文件的时候,CPU 几乎是空闲的。
通常,CPU 在等待IO操作时,可以做其他的事情的,不光是磁盘IO,网络IO也是一样的
简化程序设计
如果你打算用一个单线程的程序去处理上面的问题,你不得不时刻跟踪每个文件的读取和处理状态。而用多线程,你只需要启动两个线程,而每个线程只需负责单个文件读取和处理。这些线程会被blocked当等待从磁盘上读取文件,在等待的同时,另外的线程可以利用CPU 来处理它已经读到的数据,这样做的结果就是,磁盘一直处于繁忙的状态,不断地从磁盘读取数据到内存中。这样做,对于磁盘和CPU 的利用率得到提升,同时对于程序来说也更简单,因为每个线程只需去跟踪一个文件的读取和处理状态
响应的及时性
将单线程转变为多线程的另一个目的是更具有响应的及时性。假设一个服务端程程序在一个端口上监听请求,当收到请求后,它对这个请求进行相应的处理,然后再返回去监听另外的请求。服务端程序如下:
while(server is active){
listen for the request
process request
}
如果这个请求花费了很长的时间去处理,那么客户端就不能在此期间发送任何请求到服务端,只有当服务端处理完当前请求再次回到监听时才能接受新的请求。另一种设计是监听线程把接受到的请求交给工作线程(work thread )然后立即返回监听。工作线程会对请求进行处理,并对客户端返回一个结果。这种设计模式如下:
while(server is active){
listen for request
handle request to work thread
}
这种方式,监听线程会很快继续进行监听,因此更多的客户端可以发送更多的请求到服务端,服务端也变的更具响应性。这对桌面应用程序也是一样的。