07 非阻塞通信的基本思想


之前的accept,read的等方法都是阻塞,当没有连接或者没有数据,线程都会被阻塞在当前方法执行处。所以为了上一节为了能够处理同时和多个客户端通信,每个客户端请求来临都是分配了一个线程去处理的

jdk1.4以后,java引入了非阻塞的通信机制,服务端程序只需要启动一个线程就能完成和多个客户通信的任务。

1 线程阻塞的原因

  1. 线程执行了Thread.sleep(int n)方法,线程放弃CPU,睡眠n毫秒,然后恢复运行。
  2. 线程要执行一段同步代码,由于无法获得相关的同步锁,只好进入阻塞状态,等到获得了同步锁,才能恢复运行。
  3. 线程执行了一个对象的wait()方法,进入阻塞状态,只有等到其他线程执行了该对象的notify()或notifyAll()方法,才可能将其唤醒。
  4. 线程执行I/O操作或进行远程通信时,会因为等待相关的资源而进入阻塞状态,进行远程通信时,在客户程序中,线程在以下情况可能进入阻塞状态:
    • 请求与服务器建立连接时,会进入阻塞状态,直到连接成功。
    • 线程从Socket的输入流读入数据时,如果没有足够的数据,就会进入阻塞状态,直到读到了足够的数据,或者到达输入流的末尾,或者出现了异常,才从输入流的read()方法返回或异常中断。
    • 线程向Socket的输出流写一批数据时,可能会进入阻塞状态,等到输出了所有的数据,或者出现异常,才从输出流的write()方法返回或异常中断。
    • 当调用Socket的setSoLinger()方法设置了关闭Socket的延迟时间,那么当线程执行Socket的close()方法时,会进入阻塞状态,直到底层Socket发送完所有剩余数据,或者超过了setSoLinger()方法设置的延迟时间,才从close()方法返回。

无论是在服务器程序还是客户程序中,当通过Socket的输入流和输出流来读写数据时,都可能进入阻塞状态。这种可能出现阻塞的输入和输出操作被称为阻塞I/O。与此对照,如果执行输入和输出操作时,不会发生阻塞,则称为非阻塞I/O

2 服务器程序用多线程处理阻塞通信的局限

  1. Java虚拟机会为每个线程分配独立的堆栈空间,工作线程数目越多,系统开销就越大,而且增加了Java虚拟机调度线程的负担,增加了线程之间同步的复杂性,提高了线程死锁的可能性。
  2. 工作线程的许多时间都浪费在阻塞I/O操作上,Java虚拟机需要频繁的转让CPU的使用权,使进入阻塞状态的线程放弃CPU,再把CPU分配给处于可运行状态的线程。
  3. 工作线程并不是越多越好。保持适量的工作线程,会提高服务器的并发性能,但是当工作线程的数目到达某个极限,超出了系统的负荷时,反而会降低并发性能,使得多数客户无法快速得到服务器的响应。

3 非阻塞通信的基本思想

假如同时需要做两件事,烧开水和煮粥。烧开水的步骤如下:

放水,打开煤气
等待水烧开 // 阻塞
关闭煤气,倒入水壶

煮粥的步骤一下:

放水和米,打开煤气
等待粥煮开 // 阻塞
调整煤气,改为小火
等待粥煮熟 // 阻塞
关闭煤气

为了能够同时执行两件事,一种方案就是交给两个人去分别做这两件事,这相当于就是多线程处理。
另一个方案就是让一个人去做这件事情,这个人应该善于利用一件事情的空闲事件去做另一件事:

放水,打开煤气
放水和米,打开煤气
while(一直等待,直到有水烧开,粥煮开或者粥煮熟事件发生){
	if(水烧开){
		关闭煤气,倒入水壶
	}
	if(粥煮开){
		调整煤气,改为小火
	}
	if(粥煮熟){
		关闭煤气
	}
}

这个人不断监控烧水和煮粥的状态,如果发生水烧开,粥煮开,粥煮熟事件,就去处理对应的事情,处理完之后,回来接着监听。

服务器程序只需要一个线程就能同时负责接收客户的连接、接收各个客户发送的数据,以及向各个客户发送响应数据。服务器程序的处理流程如下:

while(一直等待,直到有接收连接就绪事件、读就绪事件或写就绪事件发生){ //阻塞
if(有客户连接)
	接收客户的连接;  //非阻塞
if(某个Socket的输入流中有可读数据)
	从输入流中读数据;  //非阻塞
if(某个Socket的输出流可以写数据)
	向输出流写数据;  //非阻塞
}

以上处理流程采用了轮询的工作方式,当某一种操作就绪,就执行该操作,否则就察看是否还有其他就绪的操作可以执行。线程不会因为某一个操作还没有就绪,就进入阻塞状态,一直傻傻的在那里等待这个操作就绪。

4 java.nio包提供了支持非阻塞通信的类

  1. ServerSocketChannel:ServerSocket的替代类,支持阻塞通信与非阻塞通信。
  2. SocketChannel:Socket的替代类,支持阻塞通信与非阻塞通信。
  3. Selector:为ServerSocketChannel监控接收连接就绪事件,为SocketChannel监控连接就绪、读就绪和写就绪事件。
  4. SelectionKey:代表ServerSocketChannel以及SocketChannel向Selector注册事件的句柄。当一个SelectionKey对象位于Selector对象selected-keys集合中,就表示与这个SelectionKey对象相关的事件发生了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值