Java NIO series of articles

  1. The underlying principles of high concurrency IO and four main IO models
  2. Four attributes and important methods of Buffer
  3. The Channel tunnel class
  4. The Selector Selector

The Buffer class and its properties

Buffer class

BufferClass is an abstract class that corresponds to the main data types in JavaNIOThere are seven buffer classes inIn fact, the most used isByteBufferBinary byte buffer type

Important properties of the Buffer class

The Buffer class contains a block of array memory of the corresponding type as the memory Buffer. To record the state and location of reads and writes, the Buffer class provides some important attributes. There are three important member attributes: Capacity, position, and limit.

In addition, there is a tag attribute: mark, in which you can temporarily store the current position; If necessary, you can revert from the mark to the Position position.

Capacity properties

The capacity property of the Buffer class, representing the size of the internal capacity. Once the number of objects written exceeds capacity, the buffer is full and cannot be written.

The Capacity property of the Buffer class cannot be changed once initialized. The size of a Buffer object cannot be changed after capacity is allocated.

As mentioned earlier, the Buffer class is abstract and Java cannot use it directly to create new objects. We must use one of the subclasses of Buffer. For example, if we use DoubleBuffer, we can write up to 100 doubles

Limit properties

The limit property of the Buffer class, representing the maximum number of reads and writes. The limit attribute is related to the read/write mode of the buffer. The meaning of the limit value is different in different modes.

  • Write mode: limit Indicates the maximum number of writes that can be performed. At the beginning of write mode, limit is set to the buffer capacity value, indicating that the buffer can always be written to full
  • Read mode: Limit indicates the maximum amount of data that can be read from the buffer.

Position attribute

The position property of the Buffer class, representing the current position. The position property is related to the read/write mode of the buffer. The value of the position property is different in different modes, and the position is adjusted when the buffer is switched between read and write modes.

  • Write mode: Position changes according to the following rules
    1. When you first enter write mode, the value of position is 0, indicating that the current write position is from scratch
    2. Each time a data is written to the buffer,position moves backwards to the next writable position,position+1
    3. The initial position is 0, and the maximum writable value position is limit-1
    4. When position reaches limit, the buffer runs out of space to write to
  • Read mode: Position changes according to the following rules
    1. When you first enter read mode, position is reset to 0, indicating that you are reading from the beginning
    2. After reading the data, position moves to the next readable position, position+1
    3. The maximum value of position is limit. When position reaches the limit, no data can be read from the buffer

Summary of 4 properties

In addition to the previous three attributes, the fourth attribute mark is simpler. Position is a temporary property that temporarily stores the value of position for subsequent reuse.

Important methods of the Buffer class

This paper mainly introduces several important methods of Buffer class, including obtaining, writing, reading, repeating reading, marking and resetting of Buffer instances.

Start by defining a public print class

  private static void logger(IntBuffer intBuffer) {
      System.out.println("capacity: " + intBuffer.capacity());
      System.out.println("limit: " + intBuffer.limit());
      System.out.println("position: " + intBuffer.position());
  }
Copy the code

Allocate () creates a buffer

    public static void allocateTest(a) {
        IntBuffer intBuffer = IntBuffer.allocate(20);
		logger(intBuffer);
    }
Copy the code

In this example, IntBuffer is a concrete Buffer subclass. By calling intBuffer.allocate (20), we create an IntBuffer instance object and allocate 20*4 bytes of memory.

Position: 0 Limit20 Capacity20Copy the code

From the above results, it can be seen that:

After a buffer is created, it is in write mode, position is written to 0, limit is initialized (20), and capacity is initialized.

Put () writes to the buffer

  public static void putTest(a) {
      IntBuffer intBuffer = IntBuffer.allocate(20);
      / / write four
      IntStream.range(1.5).forEach(intBuffer::put);
      logger(intBuffer);
  }
Copy the code

The following output is displayed:

capacity: 20
limit: 20
position: 4
Copy the code

As a result, you can see that position becomes 4, pointing to the fifth element position that can be written. Limit indicates the upper limit of the maximum number of written elements and capacity indicates the maximum capacity.

Flip flip ()

After writing to the buffer, can I read directly from the buffer? Hehe, no.

The buffer is still in write mode and needs to be converted to read mode if data needs to be read. The flip() method is an important mode shift method provided by the Buffer class. It is used to flip write mode to read mode.

  public static void flipTest(a) {
      IntBuffer intBuffer = IntBuffer.allocate(20);
      / / write four
      IntStream.range(1.5).forEach(intBuffer::put);
      logger(intBuffer);
      // Invert the buffer from write mode to read mode
      intBuffer.flip();
      logger(intBuffer);
  }
Copy the code

The following output is displayed:

capacity: 20
limit: 20
position: 4

capacity: 20
limit: 4
position: 0
Copy the code

When flip is called, position, which was previously written in mode, is changed to limit, which is readable. In the new read mode, the position value simply changes to 0, indicating that the read is started from scratch.

The flip() method from write to read conversion rules, details as follows:

  1. First, set the maximum readable length limit. Position is the last position written to the buffer in write mode as the upper limit of the buffer in read mode.
  2. Second, the value of position, the start position of the read, is set to 0, indicating that the read is beginning from scratch.
  3. Finally, the previous mark mark is cleared, because mark holds temporary locations in write mode. If you continue to use the old mark in read mode, it will cause positional confusion

For the above three steps, you can actually look at the source code for the flip method, the buffer.flip () method:

  public final Buffer flip(a) {
      limit = position;
      position = 0;
      mark = -1;
      return this;
  }
Copy the code

The new question is, how do I switch the buffer into write mode once the read is done?

The buffer.clear () clearing or Buffer.compact() compression methods can be called, which convert the Buffer to write mode.

The mode conversion of Buffer is roughly shown in the figure below:

Get () reads from the buffer

Call the flip method to switch the buffer to read mode. At this point, you can start reading data from the buffer. Reading the data is simple. The get method is called to read the data one at a time from position and adjust the corresponding buffer properties.

  public static void getTest(a) {
      IntBuffer intBuffer = IntBuffer.allocate(20);
      System.out.println("---> getTest start");
      / / write five
      IntStream.range(1.6).forEach(intBuffer::put);
      logger(intBuffer);
      // reverse write --> read
      intBuffer.flip();
      / / read 2
      IntStream.range(1.3).forEach(i -> System.out.println(intBuffer.get()));
      logger(intBuffer);
      / / read three
      IntStream.range(1.4).forEach(i -> System.out.println(intBuffer.get()));
      logger(intBuffer);

      // Now that you have read all 5 data, can you enter write mode immediately?
      // The answer is no! You need to call buffer.clear () or Buffer.compact() to clear or compress the Buffer to enter write mode
      intBuffer.compact();
      IntStream.range(1.5).forEach(intBuffer::put);
      logger(intBuffer);
  }
Copy the code

The following output is displayed:

Capacity: 20 limit: 20 position: 5 // flip() Enter read mode // Read 2 data 1 2 Capacity: 20 limit: 5 position: 2 // Read three data files 3 4 5 Capacity: 20 Limit: 5 position: 5Copy the code

From the program’s output, we can see that a read changes the value of position, but the limit value does not. If the value of position is equal to the value of limit, all data has been read, and position points to an element that has no data and can no longer be read. If you read it again, BufferUnderflowException will be thrown.

Again, can I go into write mode right after I read it? Can’t. It is still in read mode and we have to call buffer.clear () or Buffer.compact(), which clears or compacts the Buffer, to put it into write mode and make it writable again.

The rewind () rewind

You can call the rewind() method if you need to read the finished data again. Rewind () is also called rewind. It’s like playing a tape and going back and playing it again.

  public static void rewindTest(a) {
      IntBuffer intBuffer = IntBuffer.allocate(20);
      // Write 5 data
      IntStream.range(1.6).forEach(intBuffer::put);
      // Switch to read mode
      intBuffer.flip();
      // First read
      IntStream.range(1.6).forEach(i -> System.out.println(intBuffer.get()));
      logger(intBuffer);

      // IntStream.range(1, 6).forEach(i -> System.out.println(intBuffer.get()));

      Rewind position -> 0
      intBuffer.rewind();
      logger(intBuffer);

	  // Read the second time
      IntStream.range(1.6).forEach(i -> System.out.println(intBuffer.get()));
      logger(intBuffer);
  }
Copy the code

The following output is displayed:

Capacity: 20 limit: 5 position: 5 rewind() Capacity: 20 limit: 5 position: 5 0 // Read 1 2 3 4 5 Capacity: 20 LIMIT: 5 position: 5Copy the code

The rewind() method adjusts the position property of the buffer as follows:

  1. Position is reset to 0, so all data in the buffer can be reread.
  2. Limit stays the same, the amount of data is the same, and it still indicates how many elements can be read from the buffer.
  3. The mark is cleared to indicate that the previous temporary location is no longer usable.

The source code for the buffer.rewind () method is as follows:

  public final Buffer rewind(a) {
      position = 0;
      mark = -1;
      return this;
  }
Copy the code

Mark () and reset ()

The buffer.mark () method stores the value of the current position in the Mark property. The mark property remembers the temporary position. After that, the buffer.reset () method can be called to restore the value of mark to position

  public static void markAndResetTest(a) {
      IntBuffer intBuffer = IntBuffer.allocate(20);
      / / write five
      IntStream.range(1.6).forEach(intBuffer::put);
      // Switch to read
      intBuffer.flip();
	
      // Read 3 and mark position at 2
      IntStream.range(1.4).forEach(i -> {
          / / tag position
          if (i == 2) {
              intBuffer.mark();
          }
          System.out.println(intBuffer.get());
      });
      logger(intBuffer);

      // Reset position to mark
      intBuffer.reset();
      logger(intBuffer);
      
      // Read after reset
      IntStream.range(1.3).forEach(i -> {
          System.out.println(intBuffer.get());
      });
  }
Copy the code

The following output is displayed:

Capacity: 20 limit: 5 Position: 3 // reset Reset Capacity: 20 limit: 5 position: 1 // Read 2 3 after the resetCopy the code

Clear () Clears the buffer

In read mode, the clear() method is called to switch the buffer to write mode. This method clears position and sets limit to capacity, which can be written until the buffer is full.

  public final Buffer clear(a) {
      position = 0;
      limit = capacity;
      mark = -1;
      return this;
  }
Copy the code

Basic steps for using the Buffer class

In general, the basic steps for using the Java NIO Buffer class are as follows:

  1. Create an instance object of the Buffer class using the allocate() method that creates subclass instance objects.
  2. Call the PUT method to write the data to the buffer.
  3. After writing, the buffer.flip () method is called to put the Buffer into read mode before the data is read.
  4. The get method is called to read the data from the buffer.
  5. After reading, the buffer.clear () or Buffer.compact() method is called to put the Buffer into write mode.