多任务处理:管理调度Executor 接口
系统管理调度:Executor接口
在上一节中我们已经看到,将客户服务器协议的细节封装起来(如EchoProtocol.java),就可以通过同一个协议实现来使用不同的"调度"方法(如,TCPEchoServerThread.java和TCPEchoServerThreadPool.java)。实际上,对于调度方法本身来说也是这样。Executor接口(java.util.concurrent包的一部分)就代表了一个根据某种策略来执行Runnable实例的对象,其中可能包括了排队和调度的细节,或如何选择要执行的任务。Executor接口只定义了一个方法:
interface Executor {
void execute(Runnable task);
}
Java提供了大量的内置Executor接口实现,它们都可以简单方便地使用,也可以进行扩展性的配置。其中一些还提供了处理维护线程等繁琐细节的功能。例如,如果一个线程因为未捕获的异常或其他故障停止,它们就自动创建一个新的线程来替换原来的线程。
ExecutorService接口继承于Executor接口,并提供了一个更高级的工具来关闭服务器,包括正常的关闭和突然的关闭。ExecutorService还允许在完成任务后返回一个结果,这需要用到Callable接口,它和Runnable接口很像,只是多了一个返回值。
我们可以通过调用Executors类的各种静态工厂方法来获取ExecutorService实例。示例程序TCPEchoServerExecutor.java演示了基本Executor工具的使用方法。
TCPEchoServerExecutor.java
0 import java.io.IOException;
1 import java.net.ServerSocket;
2 import java.net.Socket;
3 import java.util.concurrent.Executor;
4 import java.util.concurrent.Executors;
5 import java.util.logging.Logger;
6
7 public class TCPEchoServerExecutor {
8
9 public static void main(String[] args) throws
IOException {
10
11 if (args.length != 1) { // Test for correct # of args
12 throw new IllegalArgumentException("Parameter(s):
<Port>");
13 }
14
15 int echoServPort = Integer.parseInt(args[0]); // Server
port
16
17 // Create a server socket to accept client connection
requests
18 ServerSocket servSock = new
ServerSocket(echoServPort);
19
20 Logger logger = Logger.getLogger("practical");
21
22 Executor service = Executors.newCachedThreadPool(); //
Dispatch svc
23
24 // Run forever, accepting and spawning a thread for each
connection
25 while (true) {
26 Socket clntSock = servSock.accept(); // Block waiting
for connection
27 service.execute(new EchoProtocol(clntSock, logger));
28 }
29 /* NOT REACHED */
30 }
31 }
TCPEchoServerExecutor.java
1.设置:第11-20行
端口号是唯一的参数。与前面的例子一样,我们要创建ServerSocket实例和Logger实例。这里它们不必声明为常量,因为我们不需要用匿名的Thread子类。
2.获取一个Executor实例:第22行
Executors类的newCachedThreadPool()静态工厂方法创建了一个ExecutorService实例。在使用一个实现了Runnable接口的实例调用它的execute()方法时,如果必要它将创建一个新的线程来处理任务。然而,它首先会尝试使用已有的线程。如果一个线程空闲了60秒以上,则将移出线程池。这个策略几乎总是比前面两个TCPEchoServer*例子的效率高。
3.反复循环,接收并执行连接:第25-28行
当一个新的连接请求到来时,将创建一个新的EchoProtocol实例并传递给service的execute()方法,该方法要么将其分配给一个已有的线程,要么创建一个新的线程来处理它。值得注意的是,当达到稳定状态时,缓存线程池服务最终将保持合适的线程数,以使每个线程都保持忙碌,同时又很少创建或销毁线程。
只要有一个设计为使用Executor来调度客户端的服务器,我们就可以通过简单地改变Executor实例的类型来实现不同的调度策略。例如,如果我们想使用像TCPEchoServerPool.java中那样的固定大小的线程池,只需要改变与设置调度服务相关的一行代码:
Executor service =Executors.newFixedThreadPool(threadPoolSize);
我们可以将线程池的大小设为1,转换成使用单一线程处理所有的客户端连接,或者使用以下方法来实现:
Executor service = Executors.newSingleThreadExecutor();
在Executor方法中,如果一个"工人"线程由于某些故障死掉了,Executor将创建一个新的线程来代替它。而且,任务是在 Executor 的内部排队,而不是像我们最初的服务器那样在网络系统中排队。到现在为止,我们仅仅触及到了Java并发包的表层功能而已。
相关下载:
Java_TCPIP_Socket编程(doc)
http://download.csdn.net/detail/undoner/4940239
文献来源:
UNDONER(小杰博客) :http://blog.csdn.net/undoner
LSOFT.CN(琅软中国) :http://www.lsoft.cn