Java中的阻塞和非阻塞IO包各自的优劣思考
NIO 设计背后的基石:反应器模式,用于事件多路分离和分派的体系结构模式。
反应器(Reactor):用于事件多路分离和分派的体系结构模式
通常的,对一个文件描述符指定的文件或设备, 有两种工作方式: 阻塞 与非阻塞 。所谓阻塞方式的意思是指, 当试图对该文件描述符进行读写时, 如果当时没有东西可读,或者暂时不可写, 程序就进入等待 状态, 直到有东西可读或者可写为止。而对于非阻塞状态, 如果没有东西可读, 或者不可写, 读写函数马上返回, 而不会等待 。
一种常用做法是:每建立一个Socket连接时,同时创建一个新线程对该Socket进行单独通信(采用阻塞的方式通信)。这种方式具有很高的响应速度,并且控制起来也很简单,在连接数较少的时候非常有效,但是如果对每一个连接都产生一个线程的无疑是对系统资源的一种浪费,如果连接数较多将会出现资源不足的情况。
另一种较高效的做法是:服务器端保存一个Socket连接列表,然后对这个列表进行轮询,如果发现某个Socket端口上有数据可读时(读就绪),则调用该socket连接的相应读操作;如果发现某个 Socket端口上有数据可写时(写就绪),则调用该socket连接的相应写操作;如果某个端口的Socket连接已经中断,则调用相应的析构方法关闭该端口。这样能充分利用服务器资源,效率得到了很大提高。
传统的阻塞式IO,每个连接必须要开一个线程来处理,并且没处理完线程不能退出。
非阻塞式IO,由于基于反应器模式,用于事件多路分离和分派的体系结构模式,所以可以利用线程池来处理。事件来了就处理,处理完了就把线程归还。而传统阻塞方式不能使用线程池来处理,假设当前有10000个连接,非阻塞方式可能用1000个线程的线程池就搞定了,而传统阻塞方式就需要开10000个来处理。如果连接数较多将会出现资源不足的情况。非阻塞的核心优势就在这里。
为什么会这样,下面就对他们做进一步细致具体的分析:
首先,我们来分析传统阻塞式IO的瓶颈在哪里。在连接数不多的情况下,传统IO编写容易方便使用。但是随着连接数的增多,问题传统IO就不行了。因为前面说过,传统IO处理每个连接都要消耗 一个线程,而程序的效率当线程数不多时是随着线程数的增加而增加,但是到一定的数量之后,是随着线程数的增加而减少。这里我们得出结论,传统阻塞式IO的瓶颈在于不能处理过多的连接。
然后,非阻塞式IO的出现的目的就是为了解决这个瓶颈。而非阻塞式IO是怎么实现的呢?非阻塞IO处理连接的线程数和连接数没有联系,也就是说处理10000个连接非阻塞IO不需要10000个线程,你可以用1000个也可以用2000个线程来处理。因为非阻塞IO处理连接是异步的。当某个连接发送请求到服务器,服务器把这个连接请求当作一个请求"事件",并把这个"事件"分配给相应的函数处理。我们可以把这个处理函数放到线程中去执行,执行完就把线程归还。这样一个线程就可以异步的处理多个事件。而阻塞式IO的线程的大部分时间都浪费在等待请求上了。
[b]引用自:http://javag.javaeye.com
阻塞式IO就是在进行读写的时候调用了某个方法,如read()或write()方法
在该方法执行完之前,会一直等待,直到该方法执行完毕。所谓阻塞式IO流,就是指在从数据流当中读写数据的的时候,阻塞当前线程,直到IO流可以重新使用为止,你也可以使用流的avaliableBytes()函数看看当前流当中有多少字节可以读取,这样就不会再阻塞了。
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
public class ChannelInputStream extends InputStream {
private ReadableByteChannel channel;
public ChannelInputStream(ReadableByteChannel channel) throws IllegalArgumentException {
if (channel == null ) {
throw new IllegalArgumentException( " The readable byte channel is null " );
}
this .channel = channel;
}
public int read() throws IOException {
ByteBuffer buffer = ByteBuffer.allocate( 1 );
int result = channel.read(buffer);
if (result != - 1 ) {
buffer.flip();
result = ( int ) buffer.get();
buffer.clear();
}
return result;
}
public int read( byte b[]) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(b.length);
int result = channel.read(buffer);
if (result != - 1 ) {
buffer.flip();
buffer.get(b, 0 , result);
buffer.clear();
}
return result;
}
public int read( byte b[], int off, int len) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(b.length);
int result = channel.read(buffer);
if (result != - 1 ) {
buffer.flip();
buffer.get(b, off, len > result ? result : len);
buffer.clear();
}
return result;
}
public void close() throws IOException {
channel.close();
}
}