少啰嗦!一分钟带你读懂Java的NIO和经典IO的区别

1、引言

很多初涉网络编程的程序员,在研究Java NIO(即异步IO)和经典IO(也就是常说的阻塞式IO)的API时,很快就会发现一个问题:我什么时候应该使用经典IO,什么时候应该使用NIO?

在本文中,将尝试用简明扼要的文字,阐明Java NIO和经典IO之间的差异、典型用例,以及这些差异如何影响我们的网络编程或数据传输代码的设计和实现的。

本文没有复杂理论,也没有像网上基它文章一样千篇一律的复制粘贴,有的只是接地气的通俗易懂,希望能给你带来帮助。

(本文同步发布于:http://www.52im.net/thread-2635-1-1.html

2、相关文章

Java新一代网络编程模型AIO原理及Linux系统AIO介绍

Java NIO基础视频教程、MINA视频教程、Netty快速入门视频

3、Java NIO和IO的主要区别

下表总结了Java NIO和IO之间的主要区别。我将在表格后面的部分中详细介绍每个区别。

3.1 Stream Oriented vs. Buffer Oriented

Java NIO和IO之间的第一个重要区别是IO是面向流的,其中NIO是面向缓冲区的。那么,这意味着什么?

面向流的Java IO意味着您可以从流中一次读取一个或多个字节。你对读取的字节做什么取决于你。它们不会缓存在任何地方。此外,您无法在流中的数据中前后移动。如果需要在从流中读取的数据中前后移动,则需要先将其缓存在缓冲区中。

Java NIO的面向缓冲区的方法略有不同。数据被读入缓冲区,稍后处理该缓冲区。你可以根据需要在缓冲区中前后移动。这使你在处理过程中具有更大的灵活性。但是,你还需要检查缓冲区是否包含完整处理所需的所有数据。并且,你需要确保在将更多数据读入缓冲区时,不要覆盖尚未处理的缓冲区中的数据。

3.2 Blocking vs. Non-blocking IO

Java IO的各种流都是blocking的。这意味着,当线程调用read()或write()时,该线程将被阻塞,直到有一些数据要读取,或者数据被完全写入,在此期间,该线程无法执行任何其他操作。

Java NIO的非阻塞模式允许线程请求从通道读取数据,并且只获取当前可用的内容,或者根本没有数据,如果当前没有数据可用。线程可以继续使用其他内容,而不是在数据可供读取之前保持阻塞状态。

非阻塞写入也是如此,线程可以请求将某些数据写入通道,但不要等待它完全写入。然后线程可以继续并在同一时间做其他事情。

线程在IO调用中没有阻塞时花费空闲时间,通常在此期间在其他通道上执行IO。也就是说,单个线程现在可以管理多个输入和输出通道。

4、Selectors

Java NIO的选择器允许单个线程监视多个输入通道。你可以使用选择器注册多个通道,然后使用单个线程“选择”具有可用于处理的输入的通道,或者选择准备写入的通道。这种选择器机制使单个线程可以轻松管理多个通道。

5、NIO和经典IO如何影响应用程序的设计?

选择NIO或IO作为IO工具包可能会影响应用程序设计的以下方面:

1)API调用NIO或IO类;

2)处理数据;

3)用于处理数据的线程数。

5.1 API调用

当然,使用NIO时的API调用看起来与使用IO时不同。这并不奇怪。而不是仅仅从例如InputStream读取字节的数据字节,必须首先将数据读入缓冲区,然后从那里进行处理。

5.2 数据处理

使用纯NIO设计与IO设计时,数据处理也会受到影响。

在IO设计中,您从InputStream或Reader中读取字节的数据字节。想象一下,您正在处理基于行的文本数据流。

例如:

Name: Anna

Age: 25

Email: [url=mailto:[email protected]][email protected][/url]

Phone: 1234567890

这个文本行流可以像这样处理:

InputStream input = ... ; // get the InputStream from the client socket

BufferedReader reader = newBufferedReader(newInputStreamReader(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);

intbytesRead = inChannel.read(buffer);

注意第二行从通道读取字节到ByteBuffer。当该方法调用返回时,您不知道所需的所有数据是否都在缓冲区内。你只知道缓冲区包含一些字节,这使得处理更加困难。

想象一下,在第一次读取(缓冲)调用之后,是否所有读入缓冲区的内容都是半行。例如,“姓名:An”。你能处理这些数据吗?并不是的。在完成任何数据的处理之前,您需要等待至少一整行数据进入缓冲区。

那么你怎么知道缓冲区是否包含足够的数据来处理它?好吧,你没有。找出的唯一方法是查看缓冲区中的数据。结果是,在您知道所有数据是否存在之前,您可能需要多次检查缓冲区中的数据。这既低效又可能在程序设计方面变得混乱。

例如:

ByteBuffer buffer = ByteBuffer.allocate(48);

intbytesRead = inChannel.read(buffer);

while(! bufferFull(bytesRead) ) {

bytesRead = inChannel.read(buffer);

}

bufferFull()方法必须跟踪读入缓冲区的数据量,并返回true或false,具体取决于缓冲区是否已满。换句话说,如果缓冲区已准备好进行处理,则认为它已满。

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

如果缓冲区已满,则可以对其进行处理。如果它不满,您可能能够部分处理那里的任何数据,如果这在您的特定情况下是有意义的。在许多情况下,它没有。

这个图中说明了is-data-in-buffer-ready循环:

▲ Java NIO:从通道读取数据,直到所有需要的数据都在缓冲区中

6、什么时候该用NIO?什么时候该用经典IO?

NIO允许您仅使用一个(或几个)线程来管理多个通道(网络连接或文件),但成本是解析数据可能比从阻塞流中读取数据时更复杂。

如果您需要同时管理数千个打开的连接,每个只发送一些数据,例如聊天服务器,在NIO中实现服务器可能是一个优势。同样,如果您需要与其他计算机保持大量开放连接,例如在P2P网络中,使用单个线程来管理所有出站连接可能是一个优势。

此图中说明了这一个线程,多个连接设计:

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

如果您拥有较少带宽的连接,一次发送大量数据,那么可能最经典的IO服务器实现可能是最合适的。

此图说明了经典的IO服务器设计:

▲ Java IO:经典的IO服务器设计 - 由一个线程处理的一个连接

7、更简化的理解

以众所周之的数据读取过程为例,我们来一个更简化的理解。

对于数据读取,就读取速度来说:CPU > 内存 > 硬盘。

I- 就是从硬盘到内存

O- 就是从内存到硬盘

第一种方式:从硬盘读取数据,然后程序一直等,数据读完后,继续你的操作。这种方式是最简单的,叫阻塞IO(也就是经典IO)。

第二种方式:从硬盘读取数据,然后程序继续向下执行,等数据读取完后,通知当前程序读取完成(对硬件来说叫中断,对程序来说叫回调),然后此程序可以立即处理读取的数据,也可以执行完当前操作后再对读取完的数据进行操作。

8、总而言之

还是以数据读取为例,操作系统是按块Block(块)从硬盘拿数据,就如同一个大脸盆,一下子就放入了一盆水。但是,当 Java 使用的时候,旧的 IO(经典IO)确实基于 流 Stream的,也就是虽然操作系统给我了一脸盆水,但是我得用吸管慢慢喝。

由于经典IO的重重落后理念,于是,NIO 横空出世。。。

附录:更多NIO异步网络编程资料

Java新一代网络编程模型AIO原理及Linux系统AIO介绍

有关“为何选择Netty”的11个疑问及解答

开源NIO框架八卦——到底是先有MINA还是先有Netty?

选Netty还是Mina:深入研究与对比(一)

选Netty还是Mina:深入研究与对比(二)

NIO框架入门(一):服务端基于Netty4的UDP双向通信Demo演示

NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示

NIO框架入门(三):iOS与MINA2、Netty4的跨平台UDP双向通信实战

NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战

Netty 4.x学习(一):ByteBuf详解

Netty 4.x学习(二):Channel和Pipeline详解

Netty 4.x学习(三):线程模型详解

Apache Mina框架高级篇(一):IoFilter详解

Apache Mina框架高级篇(二):IoHandler详解

MINA2 线程原理总结(含简单测试实例)

Apache MINA2.0 开发指南(中文版)[附件下载]

MINA、Netty的源代码(在线阅读版)已整理发布

解决MINA数据传输中TCP的粘包、缺包问题(有源码)

解决Mina中多个同类型Filter实例共存的问题

实践总结:Netty3.x升级Netty4.x遇到的那些坑(线程篇)

实践总结:Netty3.x VS Netty4.x的线程模型

详解Netty的安全性:原理介绍、代码演示(上篇)

详解Netty的安全性:原理介绍、代码演示(下篇)

详解Netty的优雅退出机制和原理

NIO框架详解:Netty的高性能之道

Twitter:如何使用Netty 4来减少JVM的GC开销(译文)

绝对干货:基于Netty实现海量接入的推送服务技术要点

Netty干货分享:京东京麦的生产级TCP网关技术实践总结

新手入门:目前为止最透彻的的Netty高性能原理和框架架构解析

写给初学者:Java高性能NIO框架Netty的学习方法和进阶策略

少啰嗦!一分钟带你读懂Java的NIO和经典IO的区别

>> 更多同类文章 ……

(本文同步发布于:http://www.52im.net/thread-2635-1-1.html

原文地址:https://www.cnblogs.com/imstudy/p/11083604.html

时间: 2024-09-29 11:07:53

少啰嗦!一分钟带你读懂Java的NIO和经典IO的区别的相关文章

顾嘉:5分钟带你读懂《在线》逻辑

互联网成了基础设施,是创新平台:数据成了生产资料,是自然资源 :计算成了公共服务,是能源动力. ——王坚:<在线> 在我的记忆里,云计算.物联网.大数据是近年来被媒体和产业界炒得最热的几个概念了.然而时至今日,尽管有一大批创业公司以大无畏的勇气踏上了探索前路的征途,我们依然很少在现实工作和生活中真切地感受到这几个新技术带来的变化. 除了少部分出类拔萃的企业,大部分产品依旧停留在发布会的PPT中,还没来得及面世就被资本的力量洗刷殆尽.这背后自然有“物竞天择”的无常,也有市场竞争的残酷,但从主观上

5分钟带你读懂事务隔离性与隔离级别

前言 我们在上一章节中介绍过数据库的带你了解数据库中事务的ACID特性的相关用法.本章节主要来介绍下数据库中一个非常重要的知识点事务的隔离级别.如有错误还请大家及时指出~ 问题: 事务的隔离级别有哪些? 如果并发事务没有进行隔离,会出现什么问题? 以下都是采用mysql数据库 在多个事务并发做数据库操作的时候,如果没有有效的避免机制,就会出现种种问题.大体上有以下问题: 一.引发的问题 在并发事务没有进行隔离的情况下,会发生如下问题. 问题一:脏读 脏读指一个事务读取了另外一个事务未提交的数据.

全篇干货,10分钟带你读透《参与感》

全篇干货,10分钟带你读透<参与感> 2015/11/25 阅读(3164) 评论(4) 收藏(27) 加入人人都是产品经理[起点学院]产品经理实战训练营,BAT产品总监手把手带你学产品点此查看详情! 解密互联网思维 ① 雷军眼中创业成功的三大关键因素: 选个大市场.组建最优秀的团队.拿到花不完的钱.团队第一,产品第二! ② 互联网的七字诀:专注.极致.口碑.快 专注和极致是产品目标,快是行动准则,口碑则是整个互联网思维的核心,因为用户主要是以口碑来选择产品的. ③ 互联网带来的变化包括: 其

Nginx系列教程(三)| 一文带你读懂 Nginx 的负载均衡

作者:JackTian 微信公众号:杰哥的IT之旅(ID:Jake_Internet) LAMP 系列导读 01. LAMP 系列教程(一)| 详解 Linux 环境下部署 HTTPD 服务 02. LAMP 系列教程(二)| 如何在 Linux 环境下部署 AWStats 分析系统来监控 Web 站点? 03. LAMP 系列教程(三)| 一文读懂 HTTPD 服务的访问控制 04. LAMP 系列教程(四)| MySQL 数据库系统(一) 05. LAMP 系列教程(五)| MySQL 数据

分分钟带你读懂 ButterKnife 的源码

为什么要写这一系列的博客呢? 因为在 Android 开发的过程中, 泛型,反射,注解这些知识进场会用到,几乎所有的框架至少都会用到上面的一两种知识,如 Gson 就用到泛型,反射,注解,Retrofit 也用到泛型,反射,注解 .学好这些知识对我们进阶非常重要,尤其是阅读开源框架源码或者自己开发开源框架. 前言 ButterKnife 这个开源库火了有一段时间了,刚开始它的实现原理是使用反射实现的,性能较差.再后面的 版本中逐渐使用注解+放射实现,性能提高了不少. ButterKnife是基于

【 全干货 】5 分钟带你看懂 Docker !

欢迎大家前往腾讯云社区,获取更多腾讯海量技术实践干货哦~ 作者丨唐文广:腾讯工程师,负责无线研发部地图测试. 导语:Docker,近两年才流行起来的超轻量级虚拟机,它可以让你轻松完成持续集成.自动交付.自动部署,并且实现开发环境.测试环境.运维环境三方环境的真正同步.本文从Docker定义,作用,技术架构,安装和使用等全方位带你看懂Docker. Docker是啥? 打开翻译君输入Docker 结果显示码头工人,没错!码头工人搬运的是集装箱,那么今天要讲的Docker其操作的也是集装箱,这个集装

从源码入手,一文带你读懂Spring AOP面向切面编程

之前<零基础带你看Spring源码--IOC控制反转>详细讲了Spring容器的初始化和加载的原理,后面<你真的完全了解Java动态代理吗?看这篇就够了>介绍了下JDK的动态代理. 基于这两者的实现上,这次来探索下Spring的AOP原理.虽然AOP是基于Spring容器和动态代理,但不了解这两者原理也丝毫不影响理解AOP的原理实现,因为大家起码都会用. AOP,Aspect Oriented Programming,面向切面编程.在很多时候我们写一些功能的时候,不需要用到继承这么

Nginx系列教程(二)| 一文带你读懂Nginx的正向与反向代理

作者:JackTian 微信公众号:杰哥的IT之旅(ID:Jake_Internet) LAMP 系列导读 01. LAMP 系列教程(一)| 详解 Linux 环境下部署 HTTPD 服务 02. LAMP 系列教程(二)| 如何在 Linux 环境下部署 AWStats 分析系统来监控 Web 站点? 03. LAMP 系列教程(三)| 一文读懂 HTTPD 服务的访问控制 04. LAMP 系列教程(四)| MySQL 数据库系统(一) 05. LAMP 系列教程(五)| MySQL 数据

十分钟带你看懂比特币的运行原理

有一种货币目前价值成百上千美元 ,但不是由金子.铂金或任何贵重金属制造的,这就是比特币,那么比特币的原理是什么呢? 针对不方便打开视频的小伙伴,CDA字幕组也贴心的整理了文字版本,如下: 假设有一种货币目前价值成百上千美元 ,但不是由金子.铂金或任何贵重金属制造的,实际上这不是你能放在手中或存钱罐中的钱币. 这是数字货币,意味着它只以电子的形式存在. 我说的就是比特币,比特币的原理与大多数货币很不一样. 它不依附于国家或政府,因此它没有中央发行机构或监管机构.这意味着没有组织机构决定:什么时候要