When studying both the Java NIO and IO API's, a question quickly pops into mind:
When should I use IO and when should I use NIO?
In this text I will try to shed some light on the differences between Java NIO and IO, their use cases, and how they affect the design of your code.
Main Differences of Java NIO and IO
The table below summarizes the main differences between Java NIO and IO. I will get into more detail about each difference in the sections following the table.
IO | NIO |
Stream oriented | Buffer oriented |
Blocking IO | Non blocking IO |
Selectors |
Stream Oriented vs. Buffer Oriented
The first big difference between Java NIO and IO is that IO is stream oriented, where NIO is buffer oriented. So, what does that mean?
Java IO being stream oriented means that you read one or more bytes at a time, from a stream. What you do with the read bytes is up to you. They are not cached anywhere. Furthermore, you cannot move forth and back in the data in a stream. If you need to move forth and back in the data read from a stream, you will need to cache it in a buffer first.
Java NIO's buffer oriented approach is slightly different. Data is read into a buffer from which it is later processed. You can move forth and back in the buffer as you need to. This gives you a bit more flexibility during processing. However, you also need to check if the buffer contains all the data you need in order to fully process it. And, you need to make sure that when reading more data into the buffer, you do not overwrite data in the buffer you have not yet processed.
Blocking vs. Non-blocking IO
Java IO's various streams are blocking. That means, that when a
thread invokes a read()
or write()
, that thread is blocked until there is some
data to read, or the data is fully written.
The thread can do nothing else in the meantime.
Java NIO's non-blocking mode enables a thread to request reading data from a channel, and only get what is currently available, or nothing at all, if no data is currently available. Rather than remain blocked until data becomes available for reading, the thread can go on with something else.
The same is true for non-blocking writing. A thread can request that some data be written to a channel, but not wait for it to be fully written. The thread can then go on and do something else in the mean time.
What threads spend their idle time on when not blocked in IO calls, is usually performing IO on other channels in the meantime. That is, a single thread can now manage multiple channels of input and output.
Selectors
Java NIO's selectors allow a single thread to monitor multiple channels of input. You can register multiple channels with a selector, then use a single thread to "select" the channels that have input available for processing, or select the channels that are ready for writing. This selector mechanism makes it easy for a single thread to manage multiple channels.
How NIO and IO Influences Application Design
Whether you choose NIO or IO as your IO toolkit may impact the following aspects of your application design:
- The API calls to the NIO or IO classes.
- The processing of data.
- The number of thread used to process the data.
The API Calls
Of course the API calls when using NIO look different than when
using IO. This is no surprise.
Rather than just read the data byte for byte from e.g. an InputStream
,
the data
must first be read into a buffer, and then be processed from there.
The Processing of Data
The processing of the data is also affected when using a pure NIO design, vs. an IO design.
In an IO design you read the data byte for byte from an InputStream
or
a Reader
. Imagine you were processing a stream of line
based textual data.
For instance:
Name: Anna Age: 25 Email: anna@mailserver.com Phone: 1234567890
This stream of text lines could be processed like this:
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();
Notice how the processing state is determined by how far the program
has executed. In other words,
once the first reader.readLine()
method returns, you
know for sure that a full line
of text has been read. The readLine()
blocks until a
full line is read, that's why.
You also know that this line contains the name. Similarly, when the
second readLine()
call returns, you know that this line contains the age etc.
As you can see, the program progresses only when there is new data to read, and for each step you know what that data is. Once the executing thread have progressed past reading a certain piece of data in the code, the thread is not going backwards in the data (mostly not). This principle is also illustrated in this diagram:
Java IO: Reading data from a blocking stream. |
A NIO implementation would look different. Here is a simplified example:
ByteBuffer buffer = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buffer);
Notice the second line which reads bytes from the channel into the ByteBuffer
.
When that method call returns you don't know if all the data you
need is inside the buffer.
All you know is that the buffer contains some bytes. This makes
processing somewhat harder.
Imagine if, after the first read(buffer)
call, that all
what was read into the
buffer was half a line. For instance, "Name: An". Can you process
that data? Not really.
You need to wait until at leas a full line of data has been into the
buffer, before it
makes sense to process any of the data at all.
So how do you know if the buffer contains enough data for it to make sense to be processed? Well, you don't. The only way to find out, is to look at the data in the buffer. The result is, that you may have to inspect the data in the buffer several times before you know if all the data is inthere. This is both inefficient, and can become messy in terms of program design. For instance:
ByteBuffer buffer = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buffer); while(! bufferFull(bytesRead) ) { bytesRead = inChannel.read(buffer); }
The bufferFull()
method has to keep track of how much
data is read into the buffer,
and return either true
or false
, depending
on whether the buffer is
full. In other words, if the buffer is ready for processing, it is
considered full.
The bufferFull()
method scans through the buffer, but
must leave the buffer in
the same state as before the bufferFull()
method was
called. If not, the next
data read into the buffer might not be read in at the correct
location. This is not impossible,
but it is yet another issue to watch out for.
If the buffer is full, it can be processed. If it is not full, you might be able to partially process whatever data is there, if that makes sense in your particular case. In many cases it doesn't.
The is-data-in-buffer-ready loop is illustrated in this diagram:
Java NIO: Reading data from a channel until all needed data is in buffer. |
Summary
NIO allows you to manage multiple channels (network connections or files) using only a single (or few) threads, but the cost is that parsing the data might be somewhat more complicated than when reading data from a blocking stream.
If you need to manage thousands of open connections simultanously, which each only send a little data, for instance a chat server, implementing the server in NIO is probably an advantage. Similarly, if you need to keep a lot of open connections to other computers, e.g. in a P2P network, using a single thread to manage all of your outbound connections might be an advantage. This one thread, multiple connections design is illustrated in this diagram:
Java NIO: A single thread managing multiple connections. |
If you have fewer connections with very high bandwidth, sending a lot of data at a time, perhaps a classic IO server implementation might be the best fit. This diagram illustrates a classic IO server design:
Java IO: A classic IO server design - one connection handled by one thread. |
相关推荐
Peakdet 是一个 NodeJS 库,用于检测数据中的峰谷。 Peakdet 采用的来检测峰值并将其打包为库并添加测试。...// Call the peakdet function, the second argument is the minimum differece // between the
To discover the spectra of them present obvious difference and the spectra for the blood of the different kind of the patient also present obvious differece, but the spectra of the specifieness ...
#CodeQuiz CodeQuiz 是一款使用 Kivy 框架制作的游戏。 他是一个测验由四个选项组成,Ruby、Python、Javascript 和 C#。... 提交它git commit -m 'the differece' 推送它git push origin name-your-feature 创建
32033c12-f67c-4013-a328-58ec566695d8.png
**基于MATLAB GUI界面的优化手写数字识别系统:集成预处理与BP神经网络算法的实践**,- 标题: 基于matlab的自由手写体数字识别系统 - 关键词:matlab GUI界面 手写体数字 预处理 bp神经网络 特征提取 - 简述:支持打开图片识别,可对图片进行二值化、噪声处理、图像分割、归一化、细化等图像处理方法,采用了bp神经网络算法,并以MATLAB作为编程工具实现了具有友好的图形用户界面的自由手写体数字识别系统。 实验结果表明,本方法具有较高的识别率,并具有较好的抗噪性能。 附带文档 ,matlab; GUI界面; 手写体数字; 预处理; 图像处理方法; bp神经网络; 识别率; 抗噪性能; 文档,基于MATLAB的BP神经网络手写数字识别系统
协作机器人结构设计及齿轮传动系统仿 真.pdf
"基于CEEMD-GWO-SVM算法的时间序列预测:风电、光伏、负荷预测通用解决方案",基于CEEMD+GWO+SVM的时间序列预测,风电,光伏,负荷预测,替数据就可以使用。 ,CEEMD; GWO; SVM; 时间序列预测; 风电; 光伏; 负荷预测; 替换数据,基于CEEMD-GWO-SVM算法的能源时间序列预测模型
针对NCM111、523、622及811等不同配比的硬壳三元镍钴锰酸锂电池的针刺热失控现象的Comsol模拟计算研究:可调整针刺位置的影响分析。,针刺热失控硬壳三元镍钴锰酸锂电池NCM111+523+622+811针刺热失控comsol模拟计算,可调整针刺位置。 ,针刺热失控;硬壳三元镍钴锰酸锂电池;NCM系列;comsol模拟计算;针刺位置调整;可调位置;电性能优化,针刺热失控模拟:多元镍钴锰酸锂电池硬壳结构研究
"探索Comsol模拟热流固耦合中的压缩空气模型:多物理场耦合下的应力场、温度场与渗流场研究",comsol 热流固耦合 压缩空气模型 应力场 温度场 渗流场 ,comsol;热流固耦合;压缩空气模型;应力场;温度场;渗流场,COMSOL热流固耦合分析:压缩空气模型中的应力场、温度场与渗流场
"Comsol技术:经典复古与高性能偏振分光器的完美结合",Comsol高性能偏振分光器。 经典复古小案例。 ,Comsol; 高性能; 偏振分光器; 经典复古; 小案例。,经典复古案例下的高性能Comsol偏振分光器
Matlab环境中结合经验小波变换与梯度下降回归的脉冲故障电流信号降噪新方法:融合机器学习自编码器技术的优化策略,Matlab环境下一种基于经验小波变和梯度下降回归的脉冲故障电流信号降噪方法 基于机器学习(传统自编码器和极限学习自编码器)的脉冲故障电流信号降噪。 正在整理中。 ,经验小波变换;梯度下降回归;脉冲故障电流信号降噪;Matlab环境;自编码器;极限学习自编码器,Matlab中脉冲故障电流信号的降噪新方法:经验小波与机器学习融合
基于电导分量法与扰动观测法的MPPT光伏输出模型比较与探讨,基于mppt两种方法:电导分量法,扰动观测法的光伏输出模型 ,基于MPPT; 电导分量法; 扰动观测法; 光伏输出模型;,基于电导分量与扰动观测法的MPPT光伏输出模型对比研究
8457b1084d4d7727ac9316167aba56ed.part2
OFDM系统调制下QPSK与16QAM的误码率比较分析程序,OFDM系统在QPSK与16QAM调制下,误码率比较程序 ,OFDM系统; QPSK调制; 16QAM调制; 误码率比较程序,OFDM系统调制下误码率比较程序:QPSK vs 16QAM
"No.1141 S7-200 PLC与MCGS组态构建的自动化搬运机械手组态系统研究",No.1141 S7-200 PLC和MCGS组态自动化搬运机械手的组态系统 ,S7-200 PLC; MCGS组态; 自动化搬运机械手; 组态系统; 1141 型号,"S7-200 PLC与MCGS组态系统在自动化搬运机械手中的应用"
"COMSOL仿真:固体超声导波二维模拟及汉宁窗调制5周期正弦激励信号的添加与中心频率200kHz的位移控制",COMSOL—固体超声导波二维仿真 激励信号为汉宁窗调制的5周期正弦函数,中心频率为200kHz 通过指定位移来添加激励信号 ,COMSOL;固体超声导波;二维仿真;汉宁窗调制;正弦函数;中心频率200kHz;指定位移添加激励信号。,COMSOL固体超声导波二维仿真:汉宁窗调制正弦激励信号添加
"基于多种智能算法的带时间窗与容量限制的车辆路径规划问题求解程序代码",带时间窗的车辆路径规划(VRPTW)问题 遗传算法求解程序代码,蚁群算法,粒子群算法,节约里程算法,禁忌搜索算法 考虑车辆的最大容量限制 考虑违反时间约束和容量约束的惩罚系数 以距离最优为优化目标 代码注释清楚,可改性强,可替自己的数据 代码使用matlab编写。 可以直接运行的 ,带时间窗;遗传算法;蚁群算法;粒子群算法;节约里程算法;禁忌搜索算法;车辆最大容量限制;惩罚系数;距离最优;可改性强;Matlab编写;直接运行代码。,"基于多种算法的带时间窗车辆路径规划优化程序"
西门子S7-200PLC在物料自动称量控制系统中的应用:基于称重配料混合程序的组态设计与王设计PLC程序设计,74基西门子S7-200PLC的物料自动称量控制系统称重配料物料混合程序组态设计组态设计组态王设计plc程序设计 ,核心关键词:西门子S7-200PLC;自动称量控制系统;物料混合程序;组态设计;组态王设计;PLC程序设计。,西门子S7-200PLC物料自动称量控制系统称重配料程序组态设计
"基于物理约束的神经网络:求解偏微分方程及多类型耦合方程的机器学习新方法",物理约束的神经网络 PINN 物理约束的神经网络求解偏微分方程,对基本渗流方程进行了求解,还有不同类型的方程,固体,流体,传热,以及耦合方程的求解。 机器学习与传统数值模拟的对比,适合发文章。 ,PINN; 物理约束神经网络; 偏微分方程求解; 渗流方程; 不同类型方程; 固体流体传热; 耦合方程; 机器学习; 传统数值模拟。,物理约束神经网络在偏微分方程求解中的应用
"Informer模型详解:深度解析与注释指南",Informer模型,有详细注释 ,Informer模型; 详细注释; 算法模型; 机器学习,Informer模型详解