NIOHttpd的主要任务是启动服务器。就象前面的Httpd一样,一个服务器Socket被绑定到8080端口。两者主要的区别在于,新版本的服务器使用java.nio.channels.ServerSocketChannel而不是ServerSocket。在利用bind()方法显式地把Socket绑定到端口之前,必须先打开一个管道(Channel)。然后,main()方法实例化了一个ConnectionSelector和一个Acceptor。这样,每一个ConnectionSelector都可以用一个Acceptor注册;另外,实例化Acceptor时还提供了ServerSocketChannel。
为了理解这两个线程之间的交互过程,首先我们来仔细地分析一下Acceptor。Acceptor的主要任务是接受传入的连接请求,并通过ConnectionSelector注册它们。Acceptor的构造函数调用了超类的start()方法;run()方法包含了必需的无限循环。在这个循环中,一个阻塞性的accept()方法被调用,它最终将返回一个Socket对象——这个过程几乎与Httpd的处理过程一样,但这里使用的是ServerSocketChannel的accept()方法,而不是ServerSocket的accept()方法。最后,以调用accept()方法获得的socketChannel对象为参数创建一个Connection对象,并通过ConnectionSelector的queue()方法注册它。
总而言之:Acceptor只能在一个无限循环中接受连接请求和通过ConnectionSelector注册连接。与Acceptor一样,ConnectionSelector也是一个线程。在构造函数中,它构造了一个队列,并用Selector.open()方法打开了一个java.nio.channels.Selector。Selector是整个服务器中最重要的部分之一,它使得程序能够注册连接,能够获取已经允许读取和写入操作的连接的清单。 构造函数调用start()方法之后,run()方法里面的无限循环开始执行。在这个循环中,程序调用了Selector的select()方法。这个方法一直阻塞,直到已经注册的连接之一做好了I/O操作的准备,或Selector的wakeup()方法被调用。
当ConnectionSelector线程执行select()时,没有一个Acceptor线程能够用该Selector注册连接,因为对应的方法是同步方法,理解这一点是很重要的。因此这里使用了队列,必要时Acceptor线程向队列加入连接。
紧接着把连接放入队列的操作,Acceptor调用Selector的wakeup()方法。这个调用导致ConnectionSelector线程继续执行,从正在被阻塞的select()调用返回。由于Selector不再被阻塞,ConnectionSelector现在能够从队列注册连接。在registerQueuedConnections()方法中,其实施过程如下:
|
鏀惰棌鎴愬姛鏌ョ湅鏀惰棌>>
正在阅读:Java I/O API之性能分析 (上)Java I/O API之性能分析 (上)
2004-12-13 10:07
出处:CSDN
责任编辑:huangpeidan
键盘也能翻页,试试“← →”键