一、NIO概念
Java NIO(New IO)是Java 1.4引入的一组新的IO API,它提供了一种非阻塞的、基于缓冲区的IO操作方式,相比于传统的Java IO(也称为IO流)更加高效和灵活。
Java NIO的核心组件包括:
- 缓冲区(Buffer):用于存储数据的容器,可以读写数据。
- 通道(Channel):用于数据的读写,可以从通道中读取数据,也可以将数据写入通道。
- 选择器(Selector):用于多路复用IO操作,可以同时监控多个通道的IO事件。
Java NIO的主要优点包括:
- 非阻塞IO:可以在等待IO操作完成的同时执行其他任务,提高了系统的并发性能。
- 基于缓冲区的IO:可以减少IO操作的次数,提高了IO操作的效率。
- 多路复用IO:可以同时监控多个通道的IO事件,提高了系统的吞吐量。
二、文件NIO
下面是一个简单的Java NIO示例,演示了如何使用Java NIO进行文件复制操作:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class FileCopyExample {
public static void main(String[] args) throws Exception {
FileInputStream inputStream = new FileInputStream("source.txt");
FileOutputStream outputStream = new FileOutputStream("target.txt");
FileChannel inputChannel = inputStream.getChannel();
FileChannel outputChannel = outputStream.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (inputChannel.read(buffer) != -1) {
buffer.flip();
outputChannel.write(buffer);
buffer.clear();
}
inputChannel.close();
outputChannel.close();
inputStream.close();
outputStream.close();
}
}
在这个示例中,我们使用了Java NIO的FileChannel和ByteBuffer来进行文件复制操作。首先,我们创建了一个FileInputStream和一个FileOutputStream,然后通过它们获取了对应的FileChannel。接着,我们创建了一个ByteBuffer来存储读取到的数据,然后通过循环不断从输入通道中读取数据,将数据写入输出通道中。最后,我们关闭了所有的通道和流。
需要注意的是,Java NIO的API相对于传统的Java IO来说更加复杂,需要更多的学习和理解。但是,如果能够熟练掌握Java NIO,可以大大提高系统的性能和并发能力。Java NIO(New IO)是Java 1.4引入的一组新的IO API,它提供了一种非阻塞的、基于缓冲区的IO操作方式,相比于传统的Java IO(也称为IO流)更加高效和灵活。
三、网络NIO
nio的selector是Java NIO中的一个重要组件,用于监控多个通道的状态,以便在通道就绪时进行处理。使用nio的selector可以实现非阻塞IO,提高系统的并发处理能力。
selector主要用于网络IO,虽然可以用于文件IO,但不是最优的选择。对于文件IO,更适合使用传统的阻塞IO或者NIO中的FileChannel,例子如第二点。
使用nio的selector需要以下步骤:
- 创建一个Selector对象
Selector selector = Selector.open();
- 将通道注册到Selector中
channel.register(selector, SelectionKey.OP_READ);
其中,第二个参数是一个标志位,表示对应的通道所关心的事件类型,可以是以下四种:
- SelectionKey.OP_READ:读事件就绪
- SelectionKey.OP_WRITE:写事件就绪
- SelectionKey.OP_CONNECT:连接事件就绪
- SelectionKey.OP_ACCEPT:接受连接事件就绪
- 调用Selector的select()方法进行阻塞等待
selector.select();
该方法会一直阻塞,直到至少有一个通道就绪。
- 获取就绪的通道集合
Set<SelectionKey> selectedKeys = selector.selectedKeys();
- 遍历就绪的通道集合,进行处理
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isReadable()) {
// 处理读事件
} else if (key.isWritable()) {
// 处理写事件
} else if (key.isConnectable()) {
// 处理连接事件
} else if (key.isAcceptable()) {
// 处理接受连接事件
}
keyIterator.remove();
}
- 关闭Selector
selector.close();
以上就是使用nio的selector的基本步骤。需要注意的是,在处理完一个通道后,需要将其从就绪的通道集合中移除,否则会重复处理。
以下是一个简单的Socket服务端和客户端示例,使用Selector进行非阻塞IO操作。
服务端代码:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class Server {
private static final int BUFFER_SIZE = 1024;
private static final int PORT = 8080;
public static void main(String[] args) throws IOException {
Selector selector = Selector.open();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(PORT));
serverSocketChannel.configureBlocking(false);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = server.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println("Accepted connection from " + socketChannel);
} else if (key.isReadable()) {
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
int bytesRead = socketChannel.read(buffer);
if (bytesRead == -1) {
socketChannel.close();
System.out.println("Connection closed by client: " + socketChannel);
} else {
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
String message = new String(bytes);
System.out.println("Received message from " + socketChannel + ": " + message);
}
}
}
}
}
}
客户端代码:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;
public class Client {
private static final int BUFFER_SIZE = 1024;
private static final String HOST = "localhost";
private static final int PORT = 8080;
public static void main(String[] args) throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress(HOST, PORT));
while (!socketChannel.finishConnect()) {
// Wait for connection to be established
}
System.out.println("Connected to server: " + socketChannel);
Scanner scanner = new Scanner(System.in);
while (true) {
String message = scanner.nextLine();
if (message.equals("exit")) {
break;
}
ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
socketChannel.write(buffer);
buffer.clear();
int bytesRead = socketChannel.read(buffer);
if (bytesRead == -1) {
socketChannel.close();
System.out.println("Connection closed by server: " + socketChannel);
break;
} else {
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
String response = new String(bytes);
System.out.println("Received response from server: " + response);
}
}
socketChannel.close();
}
}
在这个示例中,服务端监听8080端口,客户端连接到该端口并发送消息。服务端接收到消息后,将其打印到控制台上。客户端也会接收到来自服务端的响应,并将其打印到控制台上。
网友评论