Home  >  Article  >  Java  >  A detailed introduction to Java NIO

A detailed introduction to Java NIO

王林
王林forward
2020-10-21 16:35:211795browse

A detailed introduction to Java NIO

Java NIO mainly needs to understand the three core concepts of buffers, channels, and selectors as a supplement to Java I/O to improve the efficiency of large-volume data transmission.

(Recommended tutorial: java course)

It is best to have basic network programming knowledge before learning NIO

Java I/O flow

Java Network Programming

Java NIO: Buffer

Channel (Channel), as one of the three core concepts of NIO (buffer, channel, selector), is used in Effectively transmit data between the byte buffer and the entity (file or socket) on the other side of the channel (the core is to transmit data)

The general pattern of NIO programming is: fill the data into the send word Section buffer --> Send to channel peer file or socket through channel

Channel Basics

The purpose of using Channel is to transmit data. Before use The channel needs to be opened and the channel needs to be closed after use

Open the channel

We know that there are two major categories of I/O: File IO and Stream I/O, which correspond to There are two types of channels: file channel (FileChannel) and socket channel (SocketChannel, ServerSocketChannel, DatagramChannel)

For the socket channel, use the static factory method to open it

SocketChannel sc = SocketChannel.open();
ServerSocketChannel sc = ServerSocketChannel.open();
DatagramChannel sc = DatagramChannel.open();

For the file channel It can only be obtained by calling the getChannel() method on a RandomAccessFile, FileInputStream, and FileOutputStream object.

FileInputStream in = new FileInputStream("/tmp/a.txt");
FileChannel fc = in.getChannel();

Use channels for data transmission

The following code first puts the data to be written. to the ByteBuffer, then open the file channel and put the data in the buffer into the file channel.

//准备数据并放入字节缓冲区
ByteBuffer bf = ByteBuffer.allocate(1024);
bf.put("i am cool".getBytes());
bf.flip();
//打开文件通道
FileOutputStream out = new FileOutputStream("/tmp/a.txt");
FileChannel fc = out.getChannel();
//数据传输
fc.write(bf);
//关闭通道
fc.close();

Close the channel

Just like Socket, FileInputStream and other objects need to be closed after use, the channel also needs to be closed after use. An open channel represents a specific connection to a specific I/O service and encapsulates the state of that connection. When the channel is closed, the connection is lost and nothing is connected to it.

Blocking & Non-blocking Mode

The channel has two operating modes: blocking and non-blocking. The channel in non-blocking mode will never sleep, and the requested operation will be completed immediately. , or return a result indicating that no operation has been performed (see the description of the Socket channel for details). Only stream-oriented channels can use non-blocking mode

File channel

File channel is used to access files by calling getChannel on a RandomAccessFile, FileInputStream, FileOutputStream object () method to obtain. Calling the getChannel method returns a FileChannel object connected to the same file with the same access rights as the file object.

File access

The purpose of using file channels is to read and write files. The read and write api of the channel is as follows:

public abstract int read(ByteBuffer dst) throws IOException;
public abstract int write(ByteBuffer src) throws IOException;

The following is a paragraph Demo for reading files

//打开文件channel
RandomAccessFile f = new RandomAccessFile("/tmp/a.txt", "r");
FileChannel fc = f.getChannel();
//从channel中读取数据,直到文件尾
ByteBuffer bb = ByteBuffer.allocate(1024);
while (fc.read(bb) != -1) {
;
}
//翻转(读之前需要先进行翻转)
bb.flip();
StringBuilder builder = new StringBuilder();
//把每一个字节转为字符(ascii编码)
while (bb.hasRemaining()) {
builder.append((char) bb.get());
}
System.out.println(builder.toString());

There is a problem with the demo above: we can only read bytes, and then let the application decode them. For this problem, we can use the tool class Channels to wrap the channel into Reader and Writer. Solution; of course, we can also directly use the Reader and Writer of Java I/O stream mode to operate characters

File channel position and file hole

File channel position (position) It is the position of an ordinary file. The value of position determines which position in the file will be read or written next.

Reading data beyond the end of the file will return -1 (file EOF)

Writing data to a position beyond the end of the file will cause file holes: for example, a file currently has 10 bytes, but writing data to position=20 will cause the position between 10 and 20 to be empty. Data, this is a file hole

force operation

The force operation forces the channel to immediately apply all modifications to the disk file (to prevent system downtime and modification loss)

public abstract void force(boolean metaData) throws IOException;

Memory file mapping

FileChannel provides a map() method, which can establish a virtual between an open file and a special type of ByteBuffer (MappedByteBuffer) memory map.

Because the MappedByteBuffer object returned by the map method is a direct buffer, it is very efficient to operate files through MappedByteBuffer (especially in the case of large amounts of data transfer)

Use of MappedByteBuffer

Read files through MappedByteBuffer

FileInputStream in = new FileInputStream("/tmp/a.txt");
FileChannel fc = in.getChannel();
MappedByteBuffer mbb = fc.map(MapMode.READ_ONLY, 0, fc.size());
StringBuilder builder = new StringBuilder();
while (mbb.hasRemaining()) {
  builder.append((char) mbb.get());
}
System.out.println(builder.toString());

Three modes of MappedByteBuffer

READ_ONLY

READ_WRITE

PRIVATE

Read-only and read-write modes are easy to understand. In PRIVATE mode, the write operation writes a temporary buffer and does not actually write the file. (Copy-on-write idea)

Socket channel

Socket channel can run in non-blocking mode and is optional. These two points make us no longer think about network programming. You need to create a thread for each Socket connection, but use a single thread to manage hundreds or thousands of Socket connections.

All Socket channels will create a Socket object of an object when instantiated. The Socket channel is not responsible for protocol-related operations. Protocol-related operations are delegated to the peer socket object (such as the SocketChannel object is delegated to Socket object)

非阻塞模式

相较于传统Java Socket的阻塞模式,SocketChannel提供了非阻塞模式,以构建高性能的网络应用程序

非阻塞模式下,几乎所有的操作都是立刻返回的。比如下面的SocketChannel运行在非阻塞模式下,connect操作会立即返回,如果success为true代表连接已经建立成功了, 如果success为false, 代表连接还在建立中(tcp连接需要一些时间)。

 //打开Socket通道
 SocketChannel ch = SocketChannel.open();
 //非阻塞模式
 ch.configureBlocking(false);
 //连接服务器 
 boolean success = ch.connect(InetSocketAddress.createUnresolved("127.0.0.1", 7001));
 //轮训连接状态, 如果连接还未建立就可以做一些别的工作
 while (!ch.finishConnect()){
    //dosomething else
 }
 //连接建立, 做正事
 //do something;

ServerSocketChannel

ServerSocketChannel与ServerSocket类似,只是可以运行在非阻塞模式下

下为一个通过ServerSocketChannel构建服务器的简单例子,主要体现了非阻塞模式,核心思想与ServerSocket类似

ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.bind(new InetSocketAddress(7001));
while (true){
  SocketChannel sc = ssc.accept();
  if(sc != null){
    handle(sc);
  }else {
    Thread.sleep(1000);
  }
}

SocketChannel 与 DatagramChannel

SocketChannel 对应 Socket, 模拟TCP协议;DatagramChannel对应DatagramSocket, 模拟UDP协议

二者的使用与SeverSocketChannel大同小异,看API即可

工具类

文体通道那里我们提到过, 通过只能操作字节缓冲区, 编解码需要应用程序自己实现。如果我们想在通道上直接操作字符,我们就需要使用工具类Channels,工具类Channels提供了通道与流互相转换、通道转换为阅读器书写器的能力,具体API入下

//通道 --> 输入输出流
public static OutputStream newOutputStream(final WritableByteChannel ch);
public static InputStream newInputStream(final AsynchronousByteChannel ch);
//输入输出流 --> 通道
public static ReadableByteChannel newChannel(final InputStream in);
public static WritableByteChannel newChannel(final OutputStream out);
//通道  --> 阅读器书写器
public static Reader newReader(ReadableByteChannel ch, String csName);
public static Writer newWriter(WritableByteChannel ch, String csName);

通过将通道转换为阅读器、书写器我们就可以直接在通道上操作字符。

    RandomAccessFile f = new RandomAccessFile("/tmp/a.txt", "r");
  FileChannel fc = f.getChannel();
  //通道转换为阅读器,UTF-8编码
  Reader reader = Channels.newReader(fc, "UTF-8");
  int i = 0, s = 0;
  char[] buff = new char[1024];
  while ((i = reader.read(buff, s, 1024 - s)) != -1) {
    s += i;
  }
  for (i = 0; i < s; i++) {
    System.out.print(buff[i]);
  }

总结

通道主要分为文件通道和套接字通道。

对于文件操作:如果是大文件使用通道的文件内存映射特性(MappedByteBuffer)来有利于提升传输性能, 否则我更倾向传统的I/O流模式(字符API);对于套接字操作, 使用通道可以运行在非阻塞模式并且是可选择的,利于构建高性能网络应用程序。

相关推荐:java入门

The above is the detailed content of A detailed introduction to Java NIO. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:juejin.im. If there is any infringement, please contact admin@php.cn delete