Note: Reprinted from the Concurrent Programming Network, java nio series tutorials
The Buffer in Java NIO is used to interact with the NIO channel. As you know, data is read from the channel into the buffer and written from the buffer into the channel.
A buffer is essentially a block of memory to which data can be written and from which data can then be read. This memory is packaged as a NIO Buffer object and provides a set of methods to conveniently access this memory.
Using Buffer to read and write data generally follows the following four steps:
Write data to Buffer
Call flip()
Method
Read data from Buffer: such as reading directly or reading into Channel
Call clear()
method or compact()
method
When writing to buffer When data is written, the buffer will record how much data is written. Once you want to read data, you need to switch the Buffer from write mode to read mode through the flip() method. In read mode, all data previously written to the buffer can be read.
Once all the data has been read, the buffer needs to be cleared so that it can be written to again. There are two ways to clear the buffer: calling the clear() or compact() method. The clear() method clears the entire buffer. The compact() method will only clear the data that has been read. Any unread data is moved to the beginning of the buffer, and newly written data is placed after the unread data in the buffer.
A buffer is essentially a piece of memory into which data can be written and from which data can be read. This memory is packaged as a NIO Buffer object and provides a set of methods to conveniently access this memory.
In order to understand how Buffer works, you need to be familiar with its three properties:
capacity
position
limit
The meaning of position and limit depends on whether the Buffer is in read mode or write mode. No matter what mode the Buffer is in, the meaning of capacity is always the same.
Here is an explanation about capacity, position and limit in read and write mode. The detailed explanation is behind the illustration.
As a memory block, Buffer has a fixed size value, also called "capacity". You can only write capacity into it byte, long, char and other types. Once the Buffer is full, it needs to be emptied (by reading data or clearing data) before writing data can continue.
When you write data to the Buffer, position represents the current position. The initial position value is 0. When a byte, long, etc. data is written to the Buffer, the position will move forward to the next Buffer unit where data can be inserted. The maximum position can be capacity - 1.
When reading data, it is also read from a specific position. When the Buffer is switched from write mode to read mode, the position will be reset to 0. When data is read from the Buffer's position, the position moves forward to the next readable position.
In write mode, the limit of the Buffer indicates the maximum amount of data you can write to the Buffer. In write mode, limit is equal to the capacity of the Buffer.
When switching the Buffer to read mode, limit indicates the maximum amount of data you can read. Therefore, when switching the Buffer to read mode, limit will be set to the position value in write mode. In other words, you can read all the data written before (limit is set to the number of written data, this value is position in write mode)
Java NIO has 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, the bytes in the buffer can be manipulated through char, short, int, long, float or double types.
MappedByteBuffer is somewhat special, and will be discussed in the special chapter involving it.
To obtain a Buffer object, you must first allocate it. Every Buffer class has an allocate method. Below is an example of allocating a ByteBuffer with a capacity of 50 bytes.
ByteBuffer buf = ByteBuffer.allocate(50);
There are two ways to write data to Buffer:
Write from Channel to Buffer.
Write to Buffer through Buffer’s put() method.
When initializing the Buffer, put the byte array into the Buffer
public void test1() throws FileNotFoundException {//初始化容量ByteBuffer buf1 = ByteBuffer.allocate(48);//通过数组初始化,并将数组中的值放入缓冲区byte[] bytes = "123".getBytes(); ByteBuffer buf2 = ByteBuffer.wrap(bytes);//将通道中的读到缓冲区int bytesRead = channel.read(bf);//通过put方法写入缓冲区,put有很多重载 buf1.put(bytes); }
put方法有很多版本,允许你以不同的方式把数据写入到Buffer中。例如, 写到一个指定的位置,或者把一个字节数组写入到Buffer。
flip方法将Buffer从写模式切换到读模式。调用flip()方法会将position设回0,并将limit设置成之前position的值。
换句话说,position现在用于标记读的位置,limit表示之前写进了多少个byte、char等 —— 现在能读取多少个byte、char等。
从Buffer中读取数据有两种方式:
从Buffer读取数据到Channel。
使用get()方法从Buffer中读取数据。
//将缓冲区的数据写入通道 channel.write(buf1);//通过get方法获取缓冲区中数据 get有很多重载buf1.get();
The get method has many versions, allowing you to read data from the Buffer in different ways. For example, read from a specified position, or read data from a Buffer into a byte array.
Buffer.rewind() sets position back to 0, so you can reread all the data in the Buffer. The limit remains unchanged and still indicates how many elements (byte, char, etc.) can be read from the Buffer.
Once the data in the Buffer is read, the Buffer needs to be ready to be written again. This can be done via the clear() or compact() methods.
If the clear() method is called, position will be set back to 0 and limit will be set to the value of capacity. In other words, the Buffer is cleared. The data in the Buffer is not cleared, but these marks tell us where to start writing data into the Buffer.
If there is some unread data in the Buffer, call the clear() method, and the data will be "forgotten", which means that there will no longer be any marks to tell you which data has been read and which has not.
If there is still unread data in the Buffer and the data is needed later, but you want to write some data first, use the compact() method.
The compact() method copies all unread data to the beginning of the Buffer. Then set the position to just behind the last unread element. The limit attribute is still set to capacity like the clear() method. The Buffer is now ready for writing data, but unread data will not be overwritten.
By calling the Buffer.mark() method, you can mark a specific position in the Buffer. You can later restore to this position by calling the Buffer.reset() method. For example:
You can use equals() and compareTo() methods to compare two Buffers.
When the following conditions are met, it means that the two Buffers are equal:
Have the same type (byte, char, int, etc. ).
The number of bytes, chars, etc. remaining in the Buffer is equal.
All remaining bytes, chars, etc. in the Buffer are the same.
As you can see, equals only compares a part of the Buffer, not every element in it. In fact, it only compares the remaining elements in the Buffer.
compareTo() method compares the remaining elements (byte, char, etc.) of two Buffers. If the following conditions are met, one Buffer is considered "less than" the other Buffer:
The first unequal element is smaller than the corresponding element in the other Buffer.
All elements are equal, but the first Buffer is exhausted before the other (the first Buffer has fewer elements than the other).
(Translation: the remaining elements are the elements from position to limit)
The so-called Scatter and Gather are channels corresponding to multiple caches: reading one channel to multiple caches; writing multiple caches to one channel
Java NIO begins to support scatter/gather, scatter/gather Used to describe the operation of reading from or writing to Channel (Translator's Note: Channel is often translated as channel in Chinese).
Scatter reading from Channel means writing the read data into multiple buffers during the read operation. Therefore, Channel "scatter" the data read from Channel into multiple Buffers
Gather writing to Channel means writing data from multiple buffers to the same buffer during a write operation. A Channel, therefore, the Channel "gathers" the data in multiple Buffers and sends them to the Channel.
Scatter / gather is often used in situations where the transmitted data needs to be processed separately. For example, when transmitting a message consisting of a message header and a message body, you may scatter the message body and message header into different buffers. , so that you can easily handle the message header and message body.
Scattering Reads
Scattering Reads means reading data from one channel into multiple buffers. As described in the following figure:
注意buffer首先被插入到数组,然后再将数组作为channel.read() 的输入参数。read()方法按照buffer在数组中的顺序将从channel中读取的数据写入到buffer,当一个buffer被写满后,channel紧接着向另一个buffer中写。
Scattering Reads在移动下一个buffer前,必须填满当前的buffer,这也意味着它不适用于动态消息(译者注:消息大小不固定)。换句话说,如果存在消息头和消息体,消息头必须完成填充(例如 128byte),Scattering Reads才能正常工作。
Gathering Writes
Gathering Writes是指数据从多个buffer写入到同一个channel。如下图描述:
buffers数组是write()方法的入参,write()方法会按照buffer在数组中的顺序,将数据写入到channel,注意只有position和limit之间的数据才会被写入。因此,如果一个buffer的容量为128byte,但是仅仅包含58byte的数据,那么这58byte的数据将被写入到channel中。因此与Scattering Reads相反,Gathering Writes能较好的处理动态消息
ByteBuffer header = ByteBuffer.allocate(128); ByteBuffer body = ByteBuffer.allocate(1024); ByteBuffer[] bufferArray = { header, body }; channel.read(bufferArray); //传入Buffer数组即可//方便展示、直接写,写之前要反转bufferchannel.write(bufferArray);
The above is the detailed content of Usage of Buffer in JAVA. For more information, please follow other related articles on the PHP Chinese website!