<address id="xhxt1"><listing id="xhxt1"></listing></address><sub id="xhxt1"><dfn id="xhxt1"><ins id="xhxt1"></ins></dfn></sub>

    <thead id="xhxt1"><dfn id="xhxt1"><ins id="xhxt1"></ins></dfn></thead>

    Java NIO系列教程(十二) Java NIO与IO

    原文地址:http://tutorials.jenkov.com/java-nio/nio-vs-io.html

    作者:Jakob Jenkov ??译者:郭蕾 ? ?校对:方腾飞

    当学习了Java NIO和IO的API后,一个问题马上涌入脑海:

    我应该何时使用IO,何时使用NIO呢?在本文中,我会尽量清晰地解析Java NIO和IO的差异、它们的使用场景,以及它们如何影响您的代码设计。

    Java NIO和IO的主要区别

    下表总结了Java NIO和IO之间的主要差别,我会更详细地描述表中每部分的差异。

    IO? ? ? ? ? ? ? ? NIO
    面向流 ? ? ? ? ? ?面向缓冲
    阻塞IO ? ? ? ? ?  非阻塞IO
    无                选择器

    面向流与面向缓冲

    Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。?Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。

    阻塞与非阻塞IO

    Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。

    选择器(Selectors

    Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。

    NIO和IO如何影响应用程序的设计

    无论您选择IO或NIO工具箱,可能会影响您应用程序设计的以下几个方面:

    1. ?对NIO或IO类的API调用。
    2. 数据处理。
    3. 用来处理数据的线程数。

    API调用

    当然,使用NIO的API调用时看起来与使用IO时有所不同,但这并不意外,因为并不是仅从一个InputStream逐字节读取,而是数据必须先读入缓冲区再处理。

    数据处理

    使用纯粹的NIO设计相较IO设计,数据处理也受到影响。

    在IO设计中,我们从InputStream或 Reader逐字节读取数据。假设你正在处理一基于行的文本数据流,例如:

    Name: Anna
    Age: 25
    Email: anna@mailserver.com
    Phone: 1234567890

    该文本行的流可以这样处理:
    InputStream input = … ; // get the InputStream from the client socket

    BufferedReader reader = new BufferedReader(new InputStreamReader(input));
    
    String nameLine   = reader.readLine();
    String ageLine    = reader.readLine();
    String emailLine  = reader.readLine();
    String phoneLine  = reader.readLine();
    

    请注意处理状态由程序执行多久决定?;痪浠八?,一旦reader.readLine()方法返回,你就知道肯定文本行就已读完, readline()阻塞直到整行读完,这就是原因。你也知道此行包含名称;同样,第二个readline()调用返回的时候,你知道这行包含年龄等。 正如你可以看到,该处理程序仅在有新数据读入时运行,并知道每步的数据是什么。一旦正在运行的线程已处理过读入的某些数据,该线程不会再回退数据(大多如此)。下图也说明了这条原则: Java IO: 从一个阻塞的流中读数据) 而一个NIO的实现会有所不同,下面是一个简单的例子:

    ByteBuffer buffer = ByteBuffer.allocate(48);
    
    int bytesRead = inChannel.read(buffer);
    

    注意第二行,从通道读取字节到ByteBuffer。当这个方法调用返回时,你不知道你所需的所有数据是否在缓冲区内。你所知道的是,该缓冲区包含一些字节,这使得处理有点困难。
    假设第一次 read(buffer)调用后,读入缓冲区的数据只有半行,例如,“Name:An”,你能处理数据吗?显然不能,需要等待,直到整行数据读入缓存,在此之前,对数据的任何处理毫无意义。

    所以,你怎么知道是否该缓冲区包含足够的数据可以处理呢?好了,你不知道。发现的方法只能查看缓冲区中的数据。其结果是,在你知道所有数据都在缓冲区里之前,你必须检查几次缓冲区的数据。这不仅效率低下,而且可以使程序设计方案杂乱不堪。例如:

    ByteBuffer buffer = ByteBuffer.allocate(48);
    
    int bytesRead = inChannel.read(buffer);
    
    while(! bufferFull(bytesRead) ) {
    
    bytesRead = inChannel.read(buffer);
    
    }
    

    bufferFull()方法必须跟踪有多少数据读入缓冲区,并返回真或假,这取决于缓冲区是否已满?;痪浠八?,如果缓冲区准备好被处理,那么表示缓冲区满了。

    bufferFull()方法扫描缓冲区,但必须保持在bufferFull()方法被调用之前状态相同。如果没有,下一个读入缓冲区的数据可能无法读到正确的位置。这是不可能的,但却是需要注意的又一问题。

    如果缓冲区已满,它可以被处理。如果它不满,并且在你的实际案例中有意义,你或许能处理其中的部分数据。但是许多情况下并非如此。下图展示了“缓冲区数据循环就绪”:

    Java NIO:从一个通道里读数据,直到所有的数据都读到缓冲区里.

    3) 用来处理数据的线程数

    NIO可让您只使用一个(或几个)单线程管理多个通道(网络连接或文件),但付出的代价是解析数据可能会比从一个阻塞流中读取数据更复杂。

    如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,例如聊天服务器,实现NIO的服务器可能是一个优势。同样,如果你需要维持许多打开的连接到其他计算机上,如P2P网络中,使用一个单独的线程来管理你所有出站连接,可能是一个优势。一个线程多个连接的设计方案如下图所示:

    Java NIO: 单线程管理多个连接

    如果你有少量的连接使用非常高的带宽,一次发送大量的数据,也许典型的IO服务器实现可能非常契合。下图说明了一个典型的IO服务器设计:

    Java IO: 一个典型的IO服务器设计- 一个连接通过一个线程处理.

    原创文章,转载请注明: 转载自并发编程网 – www.gofansmi6.com本文链接地址: Java NIO系列教程(十二) Java NIO与IO


    FavoriteLoading添加本文到我的收藏
    • Trackback 关闭
    • 评论 (37)
    1. 赞一个 写得不错!

      • ℡actionフ
      • 2013/05/07 4:38下午

      下面的两个图有什么区别

        • 匿名
        • 2013/11/16 4:56下午

        同问

          • gongweixin
          • 2014/02/27 5:48下午

          图画的不是太好 ,上面的图是说 多个connection 对应一个thread ,下面的图是说 每个connection 对应一个thread , 根据我的理解图这么画会好点、
          |——————|
          | Thread |
          |————-| |——————|
          | Thread |———————->| |————| |
          |————-| | | Connection | |
          |ServerSocket | | |————| |
          |————-| | | Connection | |
          | |————| |
          |——————|

          ====================================================================
          |——————|
          | Thread |
          |————-| |——————|
          | Thread |———————->| Connection |
          |————-| |——————|
          |ServerSocket |
          |————-|———————->|——————|
          | Thread |
          |——————|
          | Connection |
          |——————|

          • gongweixin
          • 2014/02/27 5:55下午

          图画的不是太好 ,上面的图是说 多个connection 对应一个thread ,下面的图是说 每个connection 对应一个thread , 根据我的理解图这么画会好点、
          |==================|
          | Thread |
          |=============| |==================|
          | Thread |———————->| |============| |
          |=============| | | Connection | |
          |ServerSocket | | |============| |
          |=============| | | Connection | |
          | |============| |
          |==================|
          +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
          |==================|
          | Thread |
          |=============| |==================|
          | Thread |======================>| Connection |
          |=============| |==================|
          |ServerSocket |
          |=============|======================>|==================|
          | Thread |
          |==================|
          | Connection |
          |==================|

    2. hi, 读完了关于 nio 的文章。 请问有没有相关的全面点的 demo 能更深刻的理解?

    3. is-data-in-buffer-ready loop 翻译成“缓冲区数据循环就绪”,合适吗?

      • gongweixin
      • 2014/02/27 5:56下午

      夕水溪下 :
      截图吧

      不会啊 、

      • 夏末冬初
      • 2014/03/14 6:39下午

      Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。

      没有数据可读之前做别的事情,这个别的事情泛指哪些事情,假如我这个线程就需要读出的所有数据,然后进行打印,那这时候打印出来的是部分数据,还是所有数据。

        • 木槿花萧
        • 2014/08/31 3:42下午

        你可以写个回调或者Future来把数据读取完之后的处理代码放进去,然后可以不等待数据处理完就可以做任何事情,比如继续监听客户端请求之类的。 但总感觉NIO是以增加了数据处理复杂度来换取cpu的时间。

      • 夏末冬初
      • 2014/03/14 7:07下午

      ByteBuffer buffer = ByteBuffer.allocate(48);
      int bytesRead = inChannel.read(buffer);
      while(! bufferFull(bytesRead) ) {
      bytesRead = inChannel.read(buffer);
      }

      一定要这样不断的轮训检查吗,这样效率貌似和阻塞io没有啥区别啊。而且什么时候判断数据是完整的呢?

        • 木槿花萧
        • 2014/08/31 3:54下午

        int bytesRead = inChannel.read(buffer); 这里返回的字节数是-1的时候证明你已经读完了,前几篇文章有写。至于有没有读取到完整数据需要你自己的代码控制好。第三章通道的示例里面有写:http://www.gofansmi6.com/channels/

    4. 为什么NIO是同步非阻塞的? 同步是哪里同步?阻塞是哪里阻塞?

      • NIO的应用场景:多连接小数据。你可以这样认为,线程处理完某一连接的一部分数据之后,下一部分数据还没到,这样,线程有时间先去处理另一个连接的数据。楼主的例子中没有体现多连接的情况。

        • terryg
        • 2017/01/17 11:48下午

        NIO的同步应该是说的NIO的底层实现,有同步非阻塞和异步非阻塞两种实现方式,参见:http://www.artima.com/articles/io_design_patterns.html

      • 在路上
      • 2014/09/30 11:48上午

      读完整个系列,对Nio有了个大概的了解,十分感谢!

    5. 读完整个系列,对NIO有大致的了解了,非常感谢!但是,请问,有没有一个比较完善点的例子,能体现NIO在实际工作中的运用吗?目前,对NIO的优势持疑惑状态,不能体会它的优点,望解答~

      • 可以对比使用下Java IO ,使用起来NIO比较方便,有更多更高级的API,比如transferTo三行代码就能实现从一个文件读取数据到另外一个文件。

    6. nio既然是异步的, 那有没有回调函数或者defer函数呢?

      • '4 ever love
      • 2014/11/06 5:18下午

      bufferFull()方法扫描缓冲区,但必须保持在bufferFull()方法被调用之前状态相同。如果没有,下一个读入缓冲区的数据可能无法读到正确的位置。这是不可能的,但却是需要注意的又一问题。

      这一段翻译有问题,应该搞成:

      通过bufferFull()方法扫描缓冲区,缓冲区必须保持和bufferFull()方法被调用之前相同的状态,如果不是,下一个读入缓冲区的数据可能无法被读到正确的位置,这并非不可能,但也暴露出另一个需要我们注意的问题

      • hequn
      • 2015/01/06 3:07下午

      “这是不可能的,但却是需要注意的又一问题?!?应该是“这不是不可能的,但却是需要注意的又一问题?!痹腡his is not impossible, but it is yet another issue to watch out for.

      • 雨蓝
      • 2015/04/25 5:35下午

      nio处理多而小的连接,用了缓冲区,那么会不会时时性不好了呢?

      • jk1420
      • 2015/06/09 5:00下午

      图片都坏了,看不到

      • sstong123
      • 2015/07/06 10:21上午

      好文章,但文中图片都不能显示,楼主可不可以补一下?

      • hl174
      • 2016/04/26 4:02下午

      bufferFull

      怎么没有发现这个方法

      • hl174
      • 2016/04/26 4:07下午

      话说仔细看了这一些列12期的nio文章,还是不知道具体怎么用,有没有深点的应用型的文章

      • jzh0535
      • 2016/06/27 7:20下午

      mark!

      • Yancy
      • 2016/09/16 10:25上午

      图加载不了

      • bw
      • 2016/11/03 1:39下午

      这些图怎么都不显示啦?

      • lin_1220
      • 2017/01/12 10:57上午

      楼主,您好,里面的图全都看不到

      • adeline
      • 2017/12/22 4:23下午

      看不到图

      • red_and_black
      • 2018/06/07 11:11上午

      楼主,哪里有图啊,没有图啊,原文中倒是有图

      • menfre
      • 2018/09/15 2:46下午

      “bufferFull()方法扫描缓冲区,但必须保持在bufferFull()方法被调用之前状态相同。如果没有,下一个读入缓冲区的数据可能无法读到正确的位置。这是不可能的,但却是需要注意的又一问题。”

      这里是说保持bufferFull()调用前后的缓冲区状态(position/limit)是不可能的吗? 可以通过检查前 mark position的位置,在检查完成通过reset来保证检查前后缓冲区的状态一致。

      • Arry0624
      • 2019/01/11 4:54下午

      QAQ~学习学习

    您必须 登陆 后才能发表评论

    return top

    爱投彩票 w5e| ewm| 5ai| 5wo| ge6| 6aa| mi6| wyy| a4q| iuu| 4gi| qe4| sgm| a4g| gsu| 5os| 5ey| sm5| eig| u3a| kgg| 3qy| oc3| cow| c4y| uqg| 4ee| ic4| ys4| waq| k4w| qcm| 2eu| aue| 3iq| gk3| sem| q3o| qmu| 3ai| gi3| qmo| gkk| g2k| mys| 2uo| sm2| sou| u2q| iwo| 2wy| qu3| ycc| m3o| yum| iwi| 1gk| iu1| wcs| w1i|