zoukankan      html  css  js  c++  java
  • 伪异步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的出现。



  • 相关阅读:
    子类继承和调用父类的构造方法 (转)
    数组复制 System.arraycopy 与 Arrays.copyof()
    ArrayList的使用方法 (转)
    Eclipse 的debug 用法 (转)
    for each
    二维数组 排序 随机数 练习
    react 之 reflux 填坑
    react & vue 项目创建的方式
    数组实例的 copyWithin()
    es6的正则扩展笔记之修饰符
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/5100095.html
Copyright © 2011-2022 走看看