IO 与 NIO 区别
Java IO
IO 面向字节流,是阻塞的。
- Java IO 中读取数据和写入数据都是面向流(Stream),即当从流中读取、写入数据的同时也将数据写入流,流的含义在于没有缓存。如果需要获取某个数据的前一项或后一项数据,就必须主动地去缓存数据,无法直接从流中获取(面向流意味着当前只拥有一个数据流的切面),即只能顺序地从流中读取数据。
- Java IO 是阻塞的,当进行数据读写操作时,需调用的数据未准备好或当前不可写,读写操作就会阻塞直到数据准备完成或可写为止,即在这期间该线程无法做其他任何事情。
- 阻塞 IO 会使线程将大量的时间浪费在等待 IO 上。但如果在数据可用时(满足请求条件)可用立即获取并处理数据,非阻塞 IO 则必须通过重复的调用来获取全部数据。
Java IO 工作流程
由于Java IO的阻塞性质,当面对多个流的读写时需要使用多个线程来处理。
如在网络 IO 中,Server 端使用一个线程监听一个端口,一旦某个连接被 accept,创建新的线程来处理新建立的连接。
read/write 是阻塞的
NIO
NIO 面向缓冲区,基于 Selector 的非阻塞。
-
Java NIO 中数据的读写是面向缓冲区(Buffer),读取时可以将整块的数据读取到缓冲区中,在写入时可将整个缓冲区的数据一起写入。面向流的数据读写只提供了一个数据流切面,而面向缓冲区的 IO 则使我们可以看到数据的上下文,即可以便利地在缓冲区中获取某项数据的前一项或后一项数据。 需要更好地管理缓冲区。
- 不能让新的数据覆盖缓冲区未处理的有用数据
- 需要正确对缓冲区数据进行分块。分清已处理与未处理数据等
-
Java NIO 是非阻塞的,每一次数据读写调用都会立即返回,并将当前可读/可写的内容写入缓冲区,或从缓冲区中输出,即使当前没有可用数据,调用仍然会立即返回并且不对缓冲区做任何操作。
- 例如,当 A 向 B 索取 100 块钱,B 只有60块,对于Java IO 来说会一直等待直到 B 凑够100块,对于 Java NIO 来说,不管 B 此时是否有足够的钱,都要马上返回结果,即 接收 B 当前拥有的 60 块,就算 B 没钱也要返回一个结果,绝不等待。
- Java NIO 使用 Selector 实现单线程管理多个 Channel(通道),通过 select 调用,可获取已经准备好的 Channel 并进行相应的处理。
Java NIO 工作流程
Java NIO 提供 Selector 实现单个线程管理多个 channel 的功能。
Selectors
Java NIO的selectors允许一条线程去监控多个channels的输入,你可以向一个selector上注册多个channel,然后调用selector的select()方法判断是否有新的连接进来或者已经在selector上注册时channel是否有数据进入。selector的机制让一个线程管理多个channel变得简单。
select 调用可能是阻塞,也可能是非阻塞。
但 read/write 是非阻塞的。
总结:
BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
NIO:New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。
-
NIO允许你用一个单独的线程或几个线程管理很多个channels(网络的或者文件的),代价是程序的处理和处理IO相比更加复杂。
-
如果你需要同时管理成千上万的连接,但是每个连接只发送少量数据,例如一个聊天服务器,用NIO实现会更好一些,相似的,如果你需要保持很多个到其他电脑的连接,例如P2P网络,用一个单独的线程来管理所有出口连接是比较合适的。
-
如果你只有少量的连接但是每个连接都占有很高的带宽,同时发送很多数据,传统的IO会更适合
参考文章:https://blog.youkuaiyun.com/u010031673/article/details/51755075
https://blog.youkuaiyun.com/linjpg/article/details/80962453