Java NIO

2023-04-22  本文已影响0人  饱饱想要灵感

一、NIO概念

Java NIO(New IO)是Java 1.4引入的一组新的IO API,它提供了一种非阻塞的、基于缓冲区的IO操作方式,相比于传统的Java IO(也称为IO流)更加高效和灵活。

Java NIO的核心组件包括:

  1. 缓冲区(Buffer):用于存储数据的容器,可以读写数据。
  2. 通道(Channel):用于数据的读写,可以从通道中读取数据,也可以将数据写入通道。
  3. 选择器(Selector):用于多路复用IO操作,可以同时监控多个通道的IO事件。

Java NIO的主要优点包括:

  1. 非阻塞IO:可以在等待IO操作完成的同时执行其他任务,提高了系统的并发性能。
  2. 基于缓冲区的IO:可以减少IO操作的次数,提高了IO操作的效率。
  3. 多路复用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需要以下步骤:

  1. 创建一个Selector对象
Selector selector = Selector.open();
  1. 将通道注册到Selector中
channel.register(selector, SelectionKey.OP_READ);

其中,第二个参数是一个标志位,表示对应的通道所关心的事件类型,可以是以下四种:

  1. 调用Selector的select()方法进行阻塞等待
selector.select();

该方法会一直阻塞,直到至少有一个通道就绪。

  1. 获取就绪的通道集合
Set<SelectionKey> selectedKeys = selector.selectedKeys();
  1. 遍历就绪的通道集合,进行处理
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();
}
  1. 关闭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端口,客户端连接到该端口并发送消息。服务端接收到消息后,将其打印到控制台上。客户端也会接收到来自服务端的响应,并将其打印到控制台上。

上一篇下一篇

猜你喜欢

热点阅读