Java NIO(New Input/Output)是Java编程语言中的一个提供了高性能、高度可扩展的I/O操作的API。它是在Java 1.4版本中引入的,用于改进传统的Java I/O操作。
Java NIO提供了一套基于通道(Channel)和缓冲区(Buffer)的I/O机制,与传统的流(Stream)I/O相比,具有更高的效率和更低的资源消耗。它允许使用非阻塞(非阻塞)I/O,可以在单个线程中同时处理多个连接,提高了系统的并发性能。
Java NIO中的关键组件包括通道(Channel)、选择器(Selector)和缓冲区(Buffer)。通道是与I/O设备进行交互的通道,可以向通道写入或读取数据。选择器是用于高效地监控多个通道的对象,可以同时监控多个通道的状态。缓冲区是用来存储数据的对象,可以读取或写入数据。
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class NonBlockingClient {
public static void main(String[] args) throws IOException {
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
if (channel.connect(new InetSocketAddress("localhost", 8080))) {
// Handle connection immediately
sendData(channel);
} else {
// Connection is in progress
while (!channel.finishConnect()) {
// Wait until the connection is established
}
sendData(channel);
}
channel.close();
}
private static void sendData(SocketChannel channel) throws IOException {
String message = "Hello, Server!";
ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
channel.write(buffer);
}
}
解释与说明: 这个示例代码展示了如何使用非阻塞式的SocketChannel进行网络通信。首先创建一个SocketChannel对象,并设置为非阻塞模式(channel.configureBlocking(false))。然后使用channel.connect()方法尝试连接远程服务器。如果连接是立即可用的,则立即发送数据(sendData方法)。否则,我们需要在while循环中等待连接完成(channel.finishConnect()),然后再发送数据。
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class FileIOExample {
public static void main(String[] args) throws IOException {
// Open file channel in non-blocking mode
Path filePath = Paths.get("file.txt");
FileChannel channel = FileChannel.open(filePath, StandardOpenOption.READ);
channel.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (channel.read(buffer) != -1) {
buffer.flip();
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
buffer.clear();
}
channel.close();
}
}
解释与说明: 该示例代码演示了如何使用非阻塞式的FileChannel从文件中读取数据。首先打开文件通道(channel = FileChannel.open(filePath, StandardOpenOption.READ)),并将其配置为非阻塞模式(channel.configureBlocking(false))。然后创建一个ByteBuffer对象,用于读取文件中的数据。使用channel.read(buffer)方法从文件中读取数据,并在控制台上打印出来。
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class NonBlockingServerHandler implements Runnable {
private final SocketChannel channel;
private final ByteBuffer buffer;
public NonBlockingServerHandler(SocketChannel channel) {
this.channel = channel;
this.buffer = ByteBuffer.allocate(1024);
}
@Override
public void run() {
try {
while (channel.read(buffer) != -1) {
buffer.flip();
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
buffer.clear();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
解释与说明: 该示例代码展示了一个多线程处理非阻塞式SocketChannel的示例。在一个线程中,使用channel.read(buffer)方法从SocketChannel中读取数据,并处理读取到的数据。当读取到-1时,表示连接已关闭,处理结束。在finally块中关闭SocketChannel。
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;
public class ZeroCopyExample {
public static void main(String[] args) throws IOException {
FileChannel fileChannel = new FileInputStream("file.txt").getChannel();
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("localhost", 8080));
while (!socketChannel.finishConnect()) {
// Wait until the connection is established
}
long transferred = fileChannel.transferTo(0, fileChannel.size(), socketChannel);
System.out.println("Transferred bytes: " + transferred);
fileChannel.close();
socketChannel.close();
}
}
解释与说明: 该示例代码展示了使用零拷贝技术将文件内容从文件通道传输到SocketChannel。首先打开文件通道(fileChannel = new FileInputStream("file.txt").getChannel()),然后打开SocketChannel(socketChannel = SocketChannel.open())并连接到远程服务器。使用fileChannel.transferTo()方法将文件内容直接传输到SocketChannel,避免了数据复制。最后关闭文件通道和SocketChannel。
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class MemoryMappedFileExample {
public static void main(String[] args) throws IOException {
RandomAccessFile file = new RandomAccessFile("file.txt", "rw");
FileChannel channel = file.getChannel();
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, file.length());
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
buffer.put(10, (byte) '!');
buffer.force();
channel.close();
}
}
解释与说明: 该示例代码演示了如何使用内存映射文件(MappedByteBuffer)来读取和写入文件内容。首先打开RandomAccessFile对象(file = new RandomAccessFile("file.txt", "rw")),然后获取文件通道(channel = file.getChannel())。使用channel.map()方法创建一个MappedByteBuffer对象,指定文件映射模式和起始位置。可以直接通过MappedByteBuffer对象读取和写入文件内容。在操作完成后,调用buffer.force()方法确保数据被刷新到磁盘上,然后关闭文件通道。
Java NIO相较于旧的IO(BIO)有以下几个主要的优势:
非阻塞IO操作:Java NIO提供了非阻塞IO操作,使得一个线程能够处理多个请求。在旧的IO中,一个线程一次只能处理一个请求,而在NIO中,一个线程可同时管理多个请求,提高了系统的吞吐量。
选择器(Selector):选择器是Java NIO中非常重要的一个概念。它允许一个线程同时监听多个通道的事件。通过选择器,我们可以用一个线程处理多个通道的IO操作,减少了线程的开销。
缓冲区(Buffer):NIO中使用缓冲区进行数据读写。缓冲区可以直接与通道进行交互,提高了IO的效率。同时,缓冲区还提供了不同类型的视图,例如ByteBuffer、CharBuffer等,方便不同类型的数据操作。
通道(Channel):通道是Java NIO中处理输入输出的一种方式。通道可以分为可读、可写、可读可写的不同类型。通过通道,我们可以实现与文件、网络等资源的交互。
非堵塞式的文件IO:NIO中提供了非堵塞式的文件IO操作,可以异步读写文件,不需要等待IO操作完成。
内存映射文件(Memory-Mapped File):NIO中引入了内存映射文件的概念,允许文件直接映射到内存,从而实现文件的快速读写。