本文翻译自http://tutorials.jenkov.com/java-concurrency/benefits.html,机翻加人工校正,仅供学习交流。
多线程的好处
多线程最大的好处就是:
- 更好地利用CPU
- 简单的程序设计
- 程序更多的响应
- 多任务下CPU资源分配公平性
更好地利用CPU
假设有一个从本地文件系统读取并处理文件的程序,从磁盘读取一个文件需要花费5秒,处理文件花费2秒。处理两个文件的花费如下
5 seconds reading file A
2 seconds processing file A
5 seconds reading file B
2 seconds processing file B
-----------------------
14 seconds total
当从磁盘里读取文件时,CPU的时间花费在等待磁盘读取数据。CPU在这段时间里几乎是空闲的,它也可以去做其他事情。通过更改操作的顺序,可以更好的利用CPU,看看这个顺序
5 seconds reading file A
5 seconds reading file B + 2 seconds processing file A
2 seconds processing file B
-----------------------
12 seconds total
CPU等待第一个文件读取,之后它开始读取第二个文件。当第二个文件在计算机进行IO操作时,CPU处理第一个文件。记住,在等待文件从磁盘读取,CPU几乎是空闲的。
一般而言,在等待IO时,CPU可以做其他的事情。它不一定是磁盘IO,也可以网络IO,或用户在机器上的输入。网络和磁盘IO往往比CPU和内存的IO慢很多。
简单的程序设计
如果你要在一个单线程应用程序中手动编写上述读取和处理的顺序,您必须同时跟踪每个文件的读取和处理状态。现在,您可以启动两个线程,每个线程只读取和处理单个文件。在等待磁盘读取其文件时,每个线程都将被阻塞。在等待期间,其他线程可以使用CPU处理它们已经读取的文件部分。磁盘会一直处于繁忙状态,将各种文件读入内存。这样的设计更好地利用了磁盘和CPU,也更容易编程,因为每个线程只需要跟踪单个文件。
程序更多的响应
将单线程应用程序转换为多线程应用程序的另一个常见目标是实现响应性更强的应用程序。假设有一个服务器应用程序在某个端口上监听传入的请求,当接收到请求时,它处理请求,然后返回监听。服务器循环概述如下
while(server is active){
listen for request
process request
}
如果该请求需要很长时间来处理,则新客户端不可以向服务器发送该请求。只有在服务器监听时才能接收请求。另一种设计是让监听线程将请求传递给工作线程,然后马上恢复到监听状态,工作线程将处理请求并向客户端发送应答。本设计概述如下
while(server is active){
listen for request
hand request to worker thread
}
通过这种方式,服务器线程将更快地返回监听。这样更多的客户端可以发送请求。
这同样适用于桌面应用程序。如果你点击一个按钮,开始漫长的任务,执行任务的线程也是更新窗口的线程,当任务执行时,应用程序将显示无响应。现在,任务可以交给一个工作线程。工作线程忙于任务时,窗口线程是可以自由地对其他用户的请求作出回应。当工作线程完成时,它会发送信号给窗口线程。工作线程的程序设计能更加快捷的响应用户的请求。
多任务下CPU资源分配公平性
假设有一个接受客户端请求的服务器,想象一下,当一个客户端发送一个请求,需要很长时间来处理——例如10秒。如果服务器处理所有任务使用一个线程,处理缓慢的请求之后的所有请求将被迫等待,直到处理完全部请求。
通过在多个线程之间划分CPU时间和在线程之间进行切换,这样CPU就可以在多个请求之间更公平地共享执行时间。这样,即使其中一个请求很慢,也可以执行处理速度更快的其他请求。当然,因为没有单独分配CPU来处理它,这意味着执行慢的请求会更慢。但是,其他请求将花费更短的等待时间来处理,因为它们不必等待缓慢的任务完成后才能被处理。如果只有缓慢的请求需要处理,那么CPU仍然可以单独分配给慢任务。