首页 > 代码库 > 伪异步IO理解

伪异步IO理解

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

         来看一下伪异步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));//用线程池的方式来处理客户端请求
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
       
	}

}

        从表面上来看,由于线程池和消息队列都是有界的,因此,无论客户端并发连接数多大,他都不会导致线程个数过于膨胀或者内存溢出,相比传统的一连接一线程模型,是一种很好的改进,但是由于底层的通信依然采用同步阻塞模型,因此无法从根本上解决问题。

      下面让我们来深入阻塞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的出现。



伪异步IO理解