Java NIO1
发现了一个很好的学习Java的外国网站,英语都是很简单的啦,看英语舒服些,关于NIO的系列就直接参照此网站了,而且是英语的!
Java NIO (New IO,也有人叫非阻塞IO) is an alternative IO API for Java (from Java 1.4), meaning alternative to the standard Java IO and Java Networking API's. Java NIO offers a different way of working with IO than the standard IO API's.
Java NIO: Channels and Buffers
In the standard IO API you work with byte streams and character streams(面向的是字节流和字符流). In NIO you work with channels and buffers(面向的是通道和缓冲区). Data is always read from a channel into a buffer, or written from a buffer to a channel.
Java NIO: Non-blocking IO
Java NIO enables you to do non-blocking IO. For instance, a thread can ask a channel to read data into a buffer. While the channel reads data into the buffer, the thread can do something else. Once data is read into the buffer, the thread can then continue processing it.(在读数据的过程中且还没有读完,线程可以开个小差) The same is true for writing data to channels.
Java NIO: Selectors
Java NIO contains the concept of "selectors". A selector is an object that can monitor multiple channels for events (like: connection opened, data arrived etc.). Thus, a single thread can monitor multiple channels for data.
Java NIO consist of the following core components:
- Channels
- Buffers
- Selectors
Java NIO has more classes and components than these, but the Channel, Buffer and Selector forms the core of the API, in my opinion. The rest of the components, like Pipe and FileLock are merely utility classes(工具而已) to be used in conjunction with the three core components. Therefore, I'll focus on these three components in this NIO overview. The other components are explained in their own texts elsewhere in this tutorial. See the menu at the top corner of this page.
Channels and Buffers
Typically, all IO in NIO starts with a Channel. A Channel is a bit like a stream. From the Channel data can be read into a Buffer
. Data can also be written from a Buffer into Channel. Here is an illustration of that:
在IO中会有这样的语句
byte[] buffer = new byte[1024]; InputStream inputStream = new FileInputStream(new File("c:/data.txt")); inputStream.read(buffer);//inputStream就相当于NIO中的Channel
byte[] buffer = "hello world".getBytes(); OutputStream os = new FileOutputStream(new File("c:/data.txt")); os.write(buffer);
Java NIO: Channels read data into Buffers, and Buffers write data into Channels |
There are several Channel and Buffer types. Here is a list of the primary Channel implementations in Java NIO:
--FileChannel(联想一下FileInputStream)
--DatagramChannel
--SocketChannel
--ServerSocketChannel
As you can see, these channels cover UDP + TCP network IO, and file IO.
There are a few interesting interfaces accompanying these classes too, but I'll keep them out of this Java NIO overview for simplicity's sake. They'll be explained where relevant, in other texts of this Java NIO tutorial.
Here is a list of the core a implementations in Java NIO:
--ByteBuffer
--CharBuffer
--DoubleBuffer
--FloatBuffer
--IntBuffer
--LongBuffer
--ShortBuffer
These Buffer's cover the basic data types that you can send via IO: byte, short, int, long, float, double and characters.
Java NIO also has a MappedByteBuffer which is used in conjunction with memory mapped files. I'll leave this Buffer out of this overview though.
Selectors
A Selector allows a single thread to handle multiple Channel's. This is handy if your application has many connections (Channels) open, but only has low traffic on each connection. For instance, in a chat server.
Here is an illustration of a thread using a Selector to handle 3 Channel's:
Java NIO: A Thread uses a Selector to handle 3 Channel's |
To use a Selector you register the Channel's with it. Then you call it's select() method. This method will block until there is an event ready for one of the registered channels. Once the method returns, the thread can then process these events. Examples of events are incoming connection, data received etc.
Java NIO Channel
Java NIO Channels are similar to streams(NIO中的Channel相当于IO中的流) with a few differences:
- You can both read and write to a Channels(Channel是双向的。可读可写). Streams are typically one-way (read or write)(流是单向的,读或者写).
- Channels can be read and written asynchronously(异步读写).
- Channels always read to, or write from, a Buffer.
As mentioned above, you read data from a channel into a buffer(inputStream.read(buffer)), and write data from a buffer into a channel(outputStream.write(buffer)). Here is an illustration of that:
Java NIO: Channels read data into Buffers, and Buffers write data into Channels |
Channel Implementations
Here are the most important Channel implementations in Java NIO:
- FileChannel
- DatagramChannel
- SocketChannel
- ServerSocketChannel
The FileChannel reads data from and to files.
The DatagramChannel can read and write data over the network via UDP.
The SocketChannel can read and write data over the network via TCP.
The ServerSocketChannel allows you to listen for incoming TCP connections, like a web server does. For each incoming connection a ServerSocket is created.
Basic Channel Example
Here is a basic example that uses a FileChannel to read some data into a Bufer:
public class Demo { public static void main(String[] args) throws Exception { RandomAccessFile aFile = new RandomAccessFile("c:/temp.txt", "rw"); FileChannel inChannel = aFile.getChannel(); ByteBuffer buf = ByteBuffer.allocate(500); int bytesRead = inChannel.read(buf); while (bytesRead != -1) { System.out.println("Read " + bytesRead); buf.flip(); while(buf.hasRemaining()){ System.out.print((char) buf.get()); } buf.clear(); bytesRead = inChannel.read(buf); } aFile.close(); } }
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw"); FileChannel inChannel = aFile.getChannel(); ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buf); while (bytesRead != -1) { System.out.println("Read " + bytesRead); buf.flip(); while(buf.hasRemaining()){ System.out.print((char) buf.get()); } buf.clear(); bytesRead = inChannel.read(buf); } aFile.close(); Process finished with exit code 0
Notice the buf.flip() call. First you read into a Buffer. Then you flip it. Then you read out of it. I'll get into more detail about that in the next text about Buffer's
Java NIO Buffer
Java NIO Buffers are used when interacting with NIO Channels. As you know, data is read from channels into buffers, and written from buffers into channels.(可以结合IO流的read和write方法来理解)
A buffer is essentially a block of memory into which you can write data, which you can then later read again. This memory block is wrapped in a NIO Buffer object, which provides a set of methods that makes it easier to work with the memory block.
Basic Buffer Usage
Using a Buffer to read and write data typically follows this little 4-step process:
--Write data into the Buffer
--Call buffer.flip()
--Read data out of the Buffer
--Call buffer.clear() or buffer.compact()
When you write data into a buffer, the buffer keeps track of how much data you have written. Once you need to read the data, you need to switch the buffer from writing mode into reading mode using the flip() method call. In reading mode the buffer lets you read all the data written into the buffer.
Once you have read all the data, you need to clear the buffer, to make it ready for writing again. You can do this in two ways: By calling clear() or by calling compact(). The clear() method clears the whole buffer. The compact() method only clears the data which you have already read. Any unread data is moved to the beginning of the buffer, and data will now be written into the buffer after the unread data.
Here is a simple Buffer usage example, with the write, flip, read and clear operations maked in bold:
RandomAccessFile aFile = new RandomAccessFile("c:/temp.txt", "rw"); FileChannel inChannel = aFile.getChannel(); //create buffer with capacity of 48 bytes ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buf); //read into buffer. while (bytesRead != -1) { buf.flip(); //make buffer ready for read while(buf.hasRemaining()){ System.out.print((char) buf.get()); // read 1 byte at a time } buf.clear(); //make buffer ready for writing bytesRead = inChannel.read(buf); } aFile.close();
在Channel中使用Buffer实例通常不是使用构造函数构建的,而是通过调用allocate()方法创建指定容量的Buffer实例
ByteBuffer buffer = ByteBuffer.allocate(CAPACITY);
或通过包装一个已有的数组来创建
ByteBuffer buffer = ByteBuffer。wrap(byteArray)
Buffer Capacity, Position and Limit
A buffer is essentially a block of memory into which you can write data, which you can then later read again. This memory block is wrapped in a NIO Buffer object, which provides a set of methods that makes it easier to work with the memory block.
A Buffer has three properties you need to be familiar with, in order to understand how a Buffer works. These are:
--capacity
--position
--limit
The meaning of position and limit depends on whether the Buffer is in read or write mode(含义取决于是写模式还是读模式). Capacity always means the same, no matter the buffer mode.
Here is an illustration of capacity, position and limit in write and read modes. The explanation follows in the sections after the illustration.
Capacity
Being a memory block, a Buffer has a certain fixed size, also called its "capacity". You can only write capacity bytes, longs, chars etc. into the Buffer. Once the Buffer is full, you need to empty it (read the data, or clear it) before you can write more data into it.
Position
When you write data into the Buffer, you do so at a certain position. Initially the position is 0. When a byte, long etc. has been written into the Buffer the position is advanced to point to the next cell in the buffer to insert data into. Position can maximally become capacity - 1.
When you read data from a Buffer you also do so from a given position. When you flip a Buffer from writing mode to reading mode, the position is reset back to 0. As you read data from the Buffer you do so from position, and position is advanced to next position to read(移向将要读取的单元).
Limit
In write mode the limit of a Buffer is the limit of how much data you can write into the buffer. In write mode the limit is equal to the capacity of the Buffer.
When flipping the Buffer into read mode, limit means the limit of how much data you can read from the data. Therefore, when flipping a Buffer into read mode, limit is set to write position of the write mode. In other words, you can read as many bytes as were written (limit is set to the number of bytes written, which is marked by position).
Buffer Types
Java NIO comes with the following Buffer types:
--ByteBuffer
--MappedByteBuffer
--CharBuffer
--DoubleBuffer
--FloatBuffer
--IntBuffer
--LongBuffer
--ShortBuffer
As you can see, these Buffer types represent different data types. In other words, they let you work with the bytes in the buffer as char, short, int, long, float or double instead.
The MappedByteBuffer is a bit special, and will be covered in its own text.
Allocating a Buffer
To obtain a Buffer object you must first allocate it. Every Buffer class has an allocate() method that does this. Here is an example showing the allocation of a ByteBuffer, with a capacity of 48 bytes:
ByteBuffer buf = ByteBuffer.allocate(48);
Here is an example allocating a CharBuffer with space for 1024 characters:
CharBuffer buf = CharBuffer.allocate(1024);
Writing Data to a Buffer
You can write data into a Buffer in two ways:
--Write data from a Channel into a Buffer
--Write data into the Buffer yourself, via the buffer's put() methods.
Here is an example showing how a Channel can write data into a Buffer:
int bytesRead = inChannel.read(buf); //read into buffer.
Here is an example that writes data into a Buffer via the put() method:
buf.put(127);
There are many other versions of the put() method, allowing you to write data into the Buffer in many different ways. For instance, writing at specific positions, or writing an array of bytes into the buffer. See the JavaDoc for the concrete buffer implementation for more details.
flip()
The flip() method switches a Buffer from writing mode to reading mode. Calling flip() sets the position back to 0, and sets the limit to where position just was.(结合上面的图理解)
In other words, position now marks the reading position, and limit marks how many bytes, chars etc. were written into the buffer - the limit of how many bytes, chars etc. that can be read.
Reading Data from a Buffer
There are two ways you can read data from a Buffer.
--Read data from the buffer into a channel.
--Read data from the buffer yourself, using one of the get() methods.
Here is an example of how you can read data from a buffer into a channel:
//read from buffer into channel. int bytesWritten = inChannel.write(buf);
Here is an example that reads data from a Buffer using the get() method:
byte aByte = buf.get();
There are many other versions of the get() method, allowing you to read data from the Buffer in many different ways. For instance, reading at specific positions, or reading an array of bytes from the buffer. See the JavaDoc for the concrete buffer implementation for more details.
rewind()
The Buffer.rewind() sets the position back to 0, so you can reread all the data in the buffer. The limit remains untouched, thus still marking how many elements (bytes, chars etc.) that can be read from the Buffer.
clear() and compact()
Once you are done reading data out of the Buffer you have to make the Buffer ready for writing again. You can do so either by calling clear() or by calling compact().
If you call clear() the position is set back to 0 and the limit to capacity. In other words, the Buffer is cleared. The data in the Buffer is not cleared. Only the markers telling where you can write data into the Buffer are.
If there is any unread data in the Buffer when you call clear() that data will be "forgotten", meaning you no longer have any markers telling what data has been read, and what has not been read.
If there is still unread data in the Buffer, and you want to read it later, but you need to do some writing first, call compact() instead of clear().
compact() copies all unread data to the beginning of the Buffer. Then it sets position to right after the last unread element. The limit property is still set to capacity, just like clear() does. Now the Buffer is ready for writing, but you will not overwrite the unread data.
mark() and reset()
You can mark a given position in a Buffer by calling the Buffer.mark() method. You can then later reset the position back to the marked position by calling the Buffer.reset() method. Here is an example:
buffer.mark(); //call buffer.get() a couple of times, e.g. during parsing. buffer.reset(); //set position back to mark.
equals() and compareTo()
It is possible to compare two buffers using equals() and compareTo().
equals()
Two buffers are equal if:
--They are of the same type (byte, char, int etc.)
--They have the same amount of remaining bytes, chars etc. in the buffer.
--All remaining bytes, chars etc. are equal.
As you can see, equals only compares part of the Buffer, not every single element inside it. In fact, it just compares the remaining elements in the Buffer.
compareTo()
The compareTo() method compares the remaining elements (bytes, chars etc.) of the two buffers, for use in e.g. sorting routines. A buffer is considered "smaller" than another buffer if:
--The first element which is equal to the corresponding element in the other buffer, is smaller than that in the other buffer.
--All elements are equal, but the first buffer runs out of elements before the second buffer does (it has fewer elements).
Java NIO1的更多相关文章
- Java NIO1:I/O模型概述
I/O模型 在开始NIO的学习之前,先对I/O的模型有一个理解,这对NIO的学习是绝对有好处的.我画一张图,简单表示一下数据从外部磁盘向运行中进程的内存区域移动的过程: 这张图片明显忽略了很多细节,只 ...
- Java NIO1:浅谈I/O模型
一.什么是同步?什么是异步? 同步和异步的概念出来已经很久了,网上有关同步和异步的说法也有很多.以下是我个人的理解: 同步就是:如果有多个任务或者事件要发生,这些任务或者事件必须逐个地进行,一个事件或 ...
- 【转载】高性能IO设计 & Java NIO & 同步/异步 阻塞/非阻塞 Reactor/Proactor
开始准备看Java NIO的,这篇文章:http://xly1981.iteye.com/blog/1735862 里面提到了这篇文章 http://xmuzyq.iteye.com/blog/783 ...
- IO之同步、异步、阻塞、非阻塞 (2)
[原创链接: http://www.smithfox.com/?e=191, 转载请保留此声明, 谢谢! ] I/O Model 是一个很大的话题, 也是一个实践性很强的事情, 网上有各种说法和资料, ...
- 也谈同步异步I/O
也谈同步异步I/O [转自: http://www.smithfox.com/?e=191 ] I/O Model 是一个很大的话题, 也是一个实践性很强的事情, 网上有各种说法和资料, 我们必须用辩 ...
- Spark案例分析
一.需求:计算网页访问量前三名 import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} /* ...
- Java IO------------------BIO(同步阻塞)、NIO1.0(多路复用)、NIO2.0(AIO,非阻塞)
1. BIO JDK5之前, JDK的IO模式只有BIO(同步阻塞)问题: 因为阻塞的存在, 需对每个请求开启一个线程. 过多的线程切换影响操作系统性能解决: 使用线程池, 处理不过来的放入队列, 再 ...
- Java NIO 学习笔记
为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/SJQ. http://www.cnblogs.com/shijiaqi1066/p/3344148.html ...
- Java I/O不迷茫,一文为你导航!
前言:在之前的面试中,每每问到关于Java I/O 方面的东西都感觉自己吃了大亏..所以这里抢救一下..来深入的了解一下在Java之中的 I/O 到底是怎么回事..文章可能说明类的文字有点儿多,希望能 ...
随机推荐
- 为什么24位位图(真彩色)的biSizeImage不等于(biWidth*biBitCount+31)/32*4*biHeight?
规定的,规定BMP文件的像素数据是按行存储的,而且每行的字节数必须为4的倍数,如果实际的像素数据不是4的倍数咋办?这就需要字节对齐,对齐是在一行的末尾添0以补足一行的字节数为4的倍数, ( biWid ...
- python2.7之乱码问题(直接从我的csdn上复制粘贴过来的。。。)
学习python一段时间了,一直没有写过博客.就从今天开始吧! python 3之后当然不存在乱码问题了.python 2的乱码问题有时就有点头疼了.(代码均为在windows下测试) 示例:保存为t ...
- Sublime Text博客插件 --- iblog
iblog是一款 sublime 博客插件,目前只支持cnblog. 项目地址:https://github.com/iskeeter/iblog 功能介绍 新建和更新cnblog的博客 支持mark ...
- margin负值在页面布局中的应用
http://www.w3school.com.cn/tiy/t.asp 预览工具 一.左右列固定,中间列自适应布局 此例适用于左右栏宽度固定,中间栏宽度自适应的布局.由于网页的主体部分一般在中间,很 ...
- jQuery 单选按钮切换
html代码片段一: <div class="row"> <div class="col-sm-12"> <label for=& ...
- UIWebView 需改userAgent 并且加载微信公共账号
需要注意的是需要获取原来的UIWebView的User-Agent,然后拼接上自己新的User-Agent,貌似直接替换原来的无效,另外,修改User-Agent之后重新创建UIWebView加载网页 ...
- OC特有语法:分类category,给NSString增加方法计算字符串中数字的个数
1:分类的使用场景:想对一个类,扩充一些功能,而又不改变原来类的模型,也不用继承,这时OC中的特有语法:分类可以做到: 当然分类也是一个类,也需要声明和实现,声明在.h文件中,实现在.m文件中,格式如 ...
- [原创] zabbix学习之旅七:如何远程操作被监控机器
虽然我们已经创建了一个报警系统,但在实际场景中,运维人员从得到报警到实际解决问题有一定的时差,若业务系统没有做高可用,那业务不得不中断,对于某些要求严格的企业级环境,这是不可容忍的,那有没有方法能让z ...
- C#中类型分析中的常见问题 Type - 转
http://www.cnblogs.com/yuanyuan/archive/2012/08/16/2642281.html 写代码的时候经常需要分析已有类型的信息例如:分析现有类型自动生成类, 或 ...
- html+css学习笔记 4[定位]
如何让图1中的div2移动到如图2上的位置: 思路:哪些css命令能够影响盒子显示的位置呢? relative相对定位/定位偏移量 position:relative; 相对定位 a ...