Abstract: NIO is New IO, this library was introduced in JDK1.4. NIO and IO have the same function and purpose, but they are implemented in different ways. NIO mainly uses blocks, so the efficiency of NIO is much higher than that of IO.
This article is shared from HUAWEI CLOUD COMMUNITY "What is the difference between NIO and IO in 20 questions to tell you the answer [Run! JAVA] ", original author: breakDraw.
NIO is New IO, this library was introduced in JDK1.4. NIO and IO have the same function and purpose, but they are implemented in different ways. NIO mainly uses blocks, so the efficiency of NIO is much higher than that of IO.
Q: What is the difference between NIO and standard IO?
A:
- Standard IO, operates based on byte stream and character stream, and blocks IO.
- NIO operates based on channel and buffer, supports non-blocking IO, and provides selectors
§ JavaNIO core 3 components:
§ Channels
Q: Can the Channel object be read and written at the same time?
Or do you need to create input and output objects at the same time to perform read and write operations like standard IO?
A: Channel Channel is bidirectional, you can read data from the channel, you can also write data.
It can be seen that both read and write can be called, and the buffer depends on the buffer.
FileChannel fileChannel = FileChannel.open(new File("a.txt").toPath());
ByteBuffer buf = ByteBuffer.allocate(1024);
fileChannel.read(buf);
fileChannel.write(buf);
- Note that in the figure above, fileChannel.read(buf) reads the data in a.txt to buf, that is, a.txt->buf
- fileChannel.write(buf) is to write the data in buf to a.txt, that is, buf->a.txt, don't get it wrong!
- The relationship between channels and buffers
Q: Does the channel support asynchronous reading and writing?
A: Support.
Q: Does the channel's reading and writing have to rely on the buffer?
A: Generally, it depends on the buffer. But it also supports transmission between two pipes, that is, direct reading and writing between pipes.
String[] arr=new String[]{"a.txt","b.txt"};
FileChannel in=new FileInputStream(arr[0]).getChannel();
FileChannel out =new FileOutputStream(arr[1]).getChannel();
// 将a.txt中的数据直接写进b.txt中,相当于文件拷贝
in.transferTo(0, in.size(), out);
Several commonly used channels
- FileChannel
FileChannel in Java NIO is a channel connected to a file. You can read and write files through the file channel. FileChannel cannot be set to non-blocking mode, it always runs in blocking mode
Creation method
RandomAccessFile file = new RandomAccessFile("D:/aa.txt");
FileChannel fileChannel = file.getChannel();
- SocketChannel
SocketChannel in Java NIO is a channel connected to a TCP network socket. Support non-blocking mode socketChannel.configureBlocking(false). SocketChannel can be created in the following 2 ways:
Open a SocketChannel and connect to a server on the Internet. When a new connection arrives at the ServerSocketChannel, a SocketChannel will be created
Creation method
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("192.168.1.100",80));
- ServerSocketChannel
The ServerSocketChannel in Java NIO is a channel that can monitor new incoming TCP connections, just like the ServerSocket in standard IO. The ServerSocketChannel class is in the java.nio.channels package. The difference between SocketChannel and ServerSocketChannel: The former is used on the client side, the latter is used on the server side
Creation method:
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket.bind(new InetSocketAddress(80));
serverSocketChannel.configureBlocking(false);
while(true){
SocketChannel socketChannel = serverSocketChannel.accept();
if(socketChannle != null)
doSomething...
}
Buffer
- We really want to get data or write data, in fact, all operations are done through buffers.
File <-> buffer <-> data - Buffer is a buffer that can be read and written, and has two modes for reading and writing.
- The capacity attribute of the buffer limits the maximum capacity of each buffer. The following 1024 is the capacity.
ByteBuffer buf = ByteBuffer.allocate(1024);
- The buffer has a position attribute, which represents the current read and write position.
- When writing data to the buffer, the position will increase.
- Maximum position is capacity-1
- Write the data in the file corresponding to the fileChannel to the buffer, which is called write mode
- After writing, call flip to set the buffer's postion to 0, which is equivalent to preparing to read the data in the buffer (that is, call buffer.get() to get the data)
- (The name of this mode is not very good personally, it is easy to get around, you can just remember: flip is to switch from writing mode to reading mode!)
Q: When the buffer calls the flip() method to switch from writing mode to reading mode, what is the position?
A: Change to 0.
ByteBuffer buf = ByteBuffer.allocate(1024);
// 数据读到buf中,并返回数量,每次最多读1024个
int byteRead = fileChannel.read(buf);
// 输出byteRead的数量,最多为1024
System.out.println("position=" + buf.position()+", byteRead=" + byteRead);
buf.flip();
// 切换到读模式了,输出0
System.out.println("position=" + buf.position());
- The buffer has 1 limit attribute.
In write mode, the limit of the buffer is the capacity of the buffer.
Q: What is the limit when the buffer is switched from writing mode to reading mode?
A: Flip() must be called before each switch. After the switch, limit is the position in the write mode.int byteRead = fileChannel.read(buf); // 输出1024
System.out.println("limit=" + buf.limit() + ",postion=" + buf.position());
System.out.println("切换到读模式");
buf.flip();
// 输出byteRead数量
System.out.println("limit=" + buf.limit());
The result is as follows
Q: What are the ways to write data to the buf buffer?
A:
int byteRead = fileChannel.read(buf);
Reading data from the channel to buf is equivalent to writing data to the buf buffer.
buf.putChar(‘a’);
Write the character a into buf manually, and add 1 to postion.
Q: What are the ways to read data from the buf buffer?
- int bytesWrite = fileChannel.write(buf)
The data in buf is written to the pipe, which is equivalent to fileChannel reading the data in buf. - byte getByte = buf.get()
Manually read the characters in 1 buf, and increase the postion by 1.
Q: What are the methods to manually modify the current buffer's postion?
A:
- rewind() sets the postion to 0
- mark() can mark a specific position, which is equivalent to marking. After a meal operation, you can use reset() to return to the previous mark() position (just like you need to mark my blog posts!)
Q: Does one channel support multiple buffers?
A: Support. The write and read methods of the channel both support the input of a buffer array, and read and write operations are performed in sequence.
Types of Buffer:
The other 3 methods of Buffer:
- warp:
When generating a fixed ByteBuffer based on a byte[], it is illegal to use ByteBuffer.wrap(). He will directly generate a new buffer based on the byte[] array, with the same value. - slice:
Get the sliced array. - duplicate:
The Buffer object returned by calling the duplicate method is a copy of the original buffer, copying the position, limit, capacity attributes note! ! ! ! ! !
The above warp\slice\duplicte generated buffer get and put operation array is still the same as the original buffer. So modifying the copied buffer will also modify the original buffer, and vice versa.
Therefore, duplite and slice are generally used to manipulate the poistion\limit and other processing, but the original content will not change, otherwise it will cause the modification of the original buffer.§ Selector
The selector can be used to associate multiple channels in a thread and monitor events.
Q: What are the benefits of Selector in NIO?
A:
- You can use fewer threads to manage each channel.
- Reduce the resource overhead of thread context switching.
Q: What types of channels does Selector support?
A:
Support non-blocking channels.
The channel should be set to non-blocking by calling channel.configureBlocking(false) before registering.
For example, FileChannel has no way to register, it is destined to be blocked. The socketChannel can support non-blocking.
Q: When the Selector is registered, which types of events are supported for monitoring, and what are the corresponding constants? (Ah, I don’t like memory the most...)
A: There are 4 types of events that can be monitored
- Connect successfully connected to 1 server, corresponding to the constant SelectionKey.OP_CONNECT
- Accept is ready to receive new incoming connections, corresponding to the constant SelectionKey.OP_ACCEPT
- Read, there is data to read, corresponding to the constant SelectionKey.OP_READ
- Write received the data written in, corresponding to the constant SelectionKey.OP_WRITE
If you want to monitor various events for this channel, you can use the "|" bit or operator to connect the constants.
int interestingSet = Selectionkey.OP_READ | Selectionkey.OP_WRITE;
Selectionkey key = channel.register(selector,interestingSet)
- The SelectionKey key represents the registration relationship between a specific channel object and a specific selector object
Q: What types of SelectionKey collections are maintained by Selector?
A: There are three types.
(1) Registered key set
The set of keys generated by all channels associated with the selector is called the set of registered keys. Not all registered keys are still valid. This collection is returned by the keys() method and may be empty. This set of registered keys cannot be modified directly; attempting to do so will cause a java.lang.UnsupportedOperationException.
(2) Selected key set
A subset of the set of registered keys. Each member of this set is an operation in which the relevant channel is judged to be prepared by the selector (in the previous selection operation), and is included in the interest set of the key. This collection is returned by the selectedKeys() method (and may be empty).
Don't confuse the set of selected keys with the ready set. This is a collection of keys, and each key is associated with a channel that has been prepared for at least one operation. Each key has an embedded ready set, indicating that the associated channel is ready for operation. Keys can be directly removed from this collection, but cannot be added. Attempting to add an element to the set of selected keys will throw a java.lang.UnsupportedOperationException.
(3) Cancelled key set
A subset of the set of registered keys. This set contains the keys for which the cancel() method has been called (this key has been invalidated), but they have not been cancelled. This collection is a private member of the selector object, so it cannot be accessed directly.
After registration, how to use selector to process ready channels:
- Call the select() method to get the channels that are ready, the returned int value indicates how many channels are ready
- Get selectedkeys from selector
- Traverse selectedkeys
- Check whether any event is ready in each SelectionKey.
- If an event is ready, get the corresponding pipe from the key. Deal with
Similar to the following, generally one thread will be started to run the processing of this selector monitoring:
while(true) {
int readyNum = selector.select();
if (readyNum == 0) {
continue;
}
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectedKeys.iterator();
while(it.hasNext()) {
SelectionKey key = it.next();
if(key.isAcceptable()) {
// 接受连接
} else if (key.isReadable()) {
// 通道可读
} else if (key.isWritable()) {
// 通道可写
}
it.remove();
}
}
Q: The select() method is actually a blocking method, that is, it will wait when it is called until all channels have been polled. If I want to end select() early, what are the methods?
A: There are 2 ways:
After wakeup() is called, the select() method returns immediately.
close(), close the selector directly.
PS: I said that NIO is non-blocking IO, but why does it say that the select() method is blocking?
- In fact, the non-blocking of NIO refers to non-blocking IO, that is, we will not get stuck at read(), we will use the selector to query the ready state, if the state is ok.
- The query operation takes time, so select() must check all channels to tell the result, so the query operation of select is blocking.
§ Other
Q: When multiple threads read and write the same file, how to lock to ensure thread safety?
A: Use the lock function of FileChannel.
RandomAccessFile randFile = new RandomAccessFile(target, "rw");
FileChannel channel = randFile.getChannel();
// pos和siz决定加锁区域, shared指定是否是共享锁
FileLock fileLock = channel.lock(pos , size , shared);
if (fileLock!=null) {
do();
// 这里简化了,实际上应该用try-catch
fileLock.release();
}
Q: If you need to read a very large file, what buffer can be used?
A: Use MappedByteBuffer.
This buffer can be accessed by understanding a large file as a byte array (but in fact, such a large byte array is not loaded, and the actual content is placed in memory + virtual storage).
Mainly through FileChannel.map (mode, starting position, area) to generate a MappedByteBuffer. Then you can use put and get to process the byte at the corresponding position.
int length = 0x8FFFFFF;//一个byte占1B,所以共向文件中存128M的数据
try (FileChannel channel = FileChannel.open(Paths.get("src/c.txt"),
StandardOpenOption.READ, StandardOpenOption.WRITE);) {
MappedByteBuffer mapBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, length);
for(int i=0;i<length;i++) {
mapBuffer.put((byte)0);
}
for(int i = length/2;i<length/2+4;i++) {
//像数组一样访问
System.out.println(mapBuffer.get(i));
}
}
Three modes:
- MapMode.READ_ONLY (read only): Attempting to modify the resulting buffer will cause a ReadOnlyBufferException to be thrown.
- MapMode.READ_WRITE (read/write): The changes to the obtained buffer will be written to the file, and the fore() method needs to be called
- MapMode.PRIVATE (dedicated): Readable and writable, but the modified content will not be written to the file, just the change of the buffer itself.
Q: How to convert the ByteBuffer in NIO to the corresponding CharBuffer according to the correct encoding
A: Use the decode function of Charset.
ByteBuffer byteBuffer = ...;
Charset charset = Charset.forName("UTF-8");
CharBuffer charBuffer = charset.decode(byteBuffer);
If it is CharBuffer to ByteBuffer, use charset.encode.
Click to follow, and learn about Huawei Cloud's fresh technology for the first time~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。