伪异步IO理解

伪异步IO实在堵塞IO的基础上将每个client发送过来的请求由新创建的线程来处理改进为用线程池来处理。因此避免了为每个client请求创建一个新线程造成的资源耗尽问题。

来看一下伪异步IO的服务端代码:

线程池类

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; /**
* @author zhouxuejun
*
* @date 2014年10月21日 上午10:05:43
*/
public class TimerServerHandlerExecutePool {
private ExecutorService executor; public TimerServerHandlerExecutePool(int maxPoolSize, int queueSize) {
executor = new ThreadPoolExecutor(Runtime.getRuntime()
.availableProcessors(), maxPoolSize, 120L, TimeUnit.SECONDS,
new ArrayBlockingQueue<java.lang.Runnable>(queueSize));
} public void execute(java.lang.Runnable task) {
executor.execute(task);
}
}

堵塞IO服务端代码:

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket; import com.bio.demo.Server.handler.TimerServerHandler;
import com.bio.demo.threadPool.TimerServerHandlerExecutePool; /**
* @author zhouxuejun
*
* @date 2014年10月20日 下午7:08:58
*/
public class TimeServer { public static ServerSocket server=null;
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
server=new ServerSocket(8080);
Socket socket=null;
TimerServerHandlerExecutePool singleExecutor=new TimerServerHandlerExecutePool(50, 10000);
while(true){
socket=server.accept();
//new Thread(new TimerServerHandler(socket)).start();
singleExecutor.execute(new TimerServerHandler(socket));//用线程池的方式来处理client请求
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} } }

从表面上来看,因为线程池和消息队列都是有界的,因此。不管client并发连接数多大。他都不会导致线程个数过于膨胀或者内存溢出。相比传统的一连接一线程模型,是一种非常好的改进。可是因为底层的通信依旧採用同步堵塞模型。因此无法从根本上解决这个问题。

以下让我们来深入堵塞IO底层来分析,首先我们来看看java同步IO的API说明:

先来看看Java输入流,以下是InputStream类中截取的一部分代码

    /**
* Reads some number of bytes from the input stream and stores them into
* the buffer array <code>b</code>. The number of bytes actually read is
* returned as an integer. <span style="color:#FF0000;"><strong>This method blocks until input data is</strong></span>
* <span style="color:#FF0000;"><strong>available, end of file is detected, or an exception is thrown.</strong></span>
*
* <p> If the length of <code>b</code> is zero, then no bytes are read and
* <code>0</code> is returned; otherwise, there is an attempt to read at
* least one byte. If no byte is available because the stream is at the
* end of the file, the value <code>-1</code> is returned; otherwise, at
* least one byte is read and stored into <code>b</code>.
*
* <p> The first byte read is stored into element <code>b[0]</code>, the
* next one into <code>b[1]</code>, and so on. The number of bytes read is,
* at most, equal to the length of <code>b</code>. Let <i>k</i> be the
* number of bytes actually read; these bytes will be stored in elements
* <code>b[0]</code> through <code>b[</code><i>k</i><code>-1]</code>,
* leaving elements <code>b[</code><i>k</i><code>]</code> through
* <code>b[b.length-1]</code> unaffected.
*
* <p> The <code>read(b)</code> method for class <code>InputStream</code>
* has the same effect as: <pre><code> read(b, 0, b.length) </code></pre>
*
* @param b the buffer into which the data is read.
* @return the total number of bytes read into the buffer, or
* <code>-1</code> if there is no more data because the end of
* the stream has been reached.
* @exception IOException If the first byte cannot be read for any reason
* other than the end of the file, if the input stream has been closed, or
* if some other I/O error occurs.
* @exception NullPointerException if <code>b</code> is <code>null</code>.
* @see java.io.InputStream#read(byte[], int, int)
*/
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}

红色加粗部分的AIP说明,当对Socket的输入流进行读取操作的时候。它会一直堵塞下去,知道三件事情发生:

1)有数据可读

2)可用数据读取完成

3)发生空指针或者IO异常

这意味着当对方发送请求或者应答消息比較缓慢、或者网络传输教慢时,读取输入流一方的同学线程将被长时间堵塞。

以下我再从输出流进行分析,来看看输出流OutputStream类,以下是截取的部分代码:

   /**
* <span style="color:#FF0000;"><strong>Writes <code>len</code> bytes from the specified byte array</strong></span>
* <span style="color:#FF0000;"><strong>starting at offset <code>off</code> to this output stream.</strong></span>
* The general contract for <code>write(b, off, len)</code> is that
* some of the bytes in the array <code>b</code> are written to the
* output stream in order; element <code>b[off]</code> is the first
* byte written and <code>b[off+len-1]</code> is the last byte written
* by this operation.
* <p>
* The <code>write</code> method of <code>OutputStream</code> calls
* the write method of one argument on each of the bytes to be
* written out. Subclasses are encouraged to override this method and
* provide a more efficient implementation.
* <p>
* If <code>b</code> is <code>null</code>, a
* <code>NullPointerException</code> is thrown.
* <p>
* If <code>off</code> is negative, or <code>len</code> is negative, or
* <code>off+len</code> is greater than the length of the array
* <code>b</code>, then an <tt>IndexOutOfBoundsException</tt> is thrown.
*
* @param b the data.
* @param off the start offset in the data.
* @param len the number of bytes to write.
* @exception IOException if an I/O error occurs. In particular,
* an <code>IOException</code> is thrown if the output
* stream is closed.
*/
public void write(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);
}
}

红色加粗的API说明,当调用OutputStream的write方法写输出流的时候,它将会被堵塞。知道所有要发送的字节所有写入完成,或者发生异常。

通过对堵塞IOAPI输入,输出文档的分析。我们了解到堵塞IO的读和写都是同步堵塞的,堵塞的时间取决于对方IO线程的处理速度和IO的传输速度。可是我们无法保证生产环境的网络状况和对端的应用程序能足够快,一个稳定可靠性高的应用不能依赖对方的处理速度。

所以才有了NIO的出现。

上一篇:Linux一个简单的读写文件


下一篇:详解Mybatis拦截器(从使用到源码)