三分钟看Netty (2) 何为IO多路复用

粗暴的解释

从JDK 1.0开始,用户就可以通过Socket进行网络编程。在JDK 1.4后,又提供了一种全新的编程方式。

在翻阅文档的时候,相信不少人对“多路复用”这个词感到陌生和费解。

对于程序员来说,代码是更容易理解。

BIO示例:

ExecutorService executor = Excutors.newFixedThreadPollExecutor(100);//建立线程池
 ServerSocket serverSocket = new ServerSocket();//新建Socket
 serverSocket.bind(8088);//监听8088端口

 while(!Thread.currentThread.isInturrupted()){ 
           Socket socket = serverSocket.accept();//循环等待新连接进入
           executor.submit(new ConnectIOnHandler(socket));//放入线程池中处理读写任务
}


class ConnectIOnHandler extends Thread{
    private Socket socket;
    public ConnectIOnHandler(Socket socket){
       this.socket = socket;
    }
    public void run(){
      while(!Thread.currentThread.isInturrupted()&&!socket.isClosed()){  
          String someThing = socket.read();//读取对方发来的消息
          if(someThing!=null){
            //TODO 做业务处理
             socket.write();//返回结果
          }
      }
    }
}

NIO 示例:

class IoThread extends Thread{
  public void run(){
Set<SelectionKey> selectedKeys=  selector.selectedKeys();
//循环获取网络事件。
  Iterator<SelectionKey> i = selectedKeys.iterator();
       for (;;) {
           final SelectionKey k = i.next();
           final Object a = k.attachment();
           i.remove();

           if (a instanceof AbstractNioChannel) {
               processSelectedKey(k, (AbstractNioChannel) a);
           }
}

代码分析

在OIO中,将新连接放入到线程池中,利用多线程处理实际的读写任务。其中Socket的accept、read、write皆为阻塞操作。可以简单的认为每一个Socket连接对应了操作系统的一个小文件,对Socket的读写就是对这个文件的读写。这是一路一线程,或者是多路一线程的概念。

而在NIO中,通过Selector将Channel和event统一管理了起来。一次selector操作可以获取一个或者多个网络连接中的读写事件。这便是是多路复用的由来,它可以同时对多个网络连接进行操作

为何引入NIO

在OIO编程模式中,通过线程池的引入,解决了OIO阻塞而引发的并行效率问题。看似已经美好,但是互联网的发展和用户量的提升,这种古老的编程模式遭受了挑战。

在OIO中,很难提升并发量。每一个新连接都需要一个Socket对象,虽然共用了线程池,但是为了保证响应性,需要设置较大的线程数。由于线程资源本身昂贵,过量的线程又会导致CPU资源竞争激烈,降低服务性能。OIO的特性决定了它必然无法支撑较大的并发量。

在NIO中,通过多路复用机制,解决了需要非常多的线程专注IO读写的问题,降低了服务的负载。从而提升了服务器的并发量。

多路复用使得服务的并发性上升了一个台阶,进一步的提升还有一个挑战。下一章将对此进行详细阐述。

猜你喜欢

转载自my.oschina.net/u/992559/blog/1632539