前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >在Java中使用NIO进行异步IO编程

在Java中使用NIO进行异步IO编程

作者头像
用户1289394
发布2023-12-30 14:07:06
1170
发布2023-12-30 14:07:06
举报
文章被收录于专栏:Java学习网Java学习网

Java NIO(New IO)是Java平台自1.4版本以来提供的一种用于处理IO操作的新API。相比旧的传统IO(即java.io包中的API),它能够更好地处理大量的并发IO操作。NIO最常用的用例之一就是创建高效的异步IO程序。

在使用Java NIO进行异步IO编程时,与传统IO模型不同的是,应用程序需要运行一个Reactor线程和多个Worker线程。Reactor线程是主要的调度员,负责注册所有I/O事件,并将这些事件分派给正确的Wroker线程。Worker线程,顾名思义,是执行实际I/O 和数据处理工作的线程。下面我们来详细介绍如何使用Java NIO进行异步IO编程的过程。

1、创建一个Selector

当一个SocketChannel或者ServerSocketChannel被创建时,它们都会分别注册到一个Selector中去。Selector则是Java NIO异步IO中最重要的概念之一。Selector对象允许线程等待一组通道中其中之一的事件发生。因此,Selector可以允许单个线程同时处理多个网络连接。

下面是一个简单的创建Selector实例的示例:

代码语言:javascript
复制
Selector selector = Selector.open(); // 创建一个Selector
channel.configureBlocking(false); // 将其设置为非阻塞模式
SelectionKey key = channel.register(selector, SelectionKey.OP_ACCEPT); // 注册 Selector,关心 Accept 事件

当你收到一个连接时,你需要做的就是注册该连接的读写事件:

代码语言:javascript
复制
SelectionKey key = channel.register(selector, Selectionkey.OP_READ); // 注册 Selector,关心 Read 事件

通过上述示例,我们创建了一个Selector对象并用它来注册SocketChannel。在这个过程中,使用代码把通道设置成非阻塞模式(即使信道不一定立即就准备好),并且我们将仅对Accept事件感兴趣。

2、接受新的连接

接下来,我们需要使用Java NIO处理新连接。当一个ServerSocketChannel准备好接受新的套接字连接时,就会向Selector发送一个信号,让其处理此类请求。与此相同,只要有数据可供读取或写入,则会自动向选择器发送信号。为了在我们正在等待中进行有效的工作, 我们需要调用select() 方法来确定发生了什么。

以下是如何接受新连接并为每个新客户端创建新通道的示例代码:

代码语言:javascript
复制
while (true) {
    int i = selector.select();
    if (i == 0) continue;

    Set<SelectionKey> selectionKeys = selector.selectedKeys();
    Iterator<SelectionKey> iterator = selectionKeys.iterator();

    while (iterator.hasNext()) {
        SelectionKey key = iterator.next();
        if (key.isAcceptable()) {
            ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
            SocketChannel channel = serverChannel.accept();
            channel.configureBlocking(false);
            SelectionKey selectionKey = channel.register(selector, Selectionkey.OP_READ);
        }
    }
}

3、从连接中读数据

当你已经成功地通过Java NIO建立了一个连接后,接下来的步骤就是开始读取数据。我们需要将客户端的请求消息(例如http请求或者其他一些协议)的内容存储在ByteBuffer对象中,并从通道上读取它。读取请求时同样需要考虑非阻塞I/O。

代码语言:javascript
复制
while (true) {
    int i = selector.select();
    if (i == 0) continue;

    Set<SelectionKey> selectionKeys = selector.selectedKeys();
    Iterator<SelectionKey> iterator = selectionKeys.iterator();

    while (iterator.hasNext()) {
        SelectionKey key = iterator.next();
        if (key.isAcceptable()) {
            ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
            SocketChannel channel = serverChannel.accept();
            channel.configureBlocking(false);
            SelectionKey selectionKey = channel.register(selector, Selectionkey.OP_READ);
        } else if (key.isReadable()) {
            SocketChannel channel = (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int readBytes = channel.read(buffer);
            if (readBytes > 0) {
                buffer.flip(); // 将其设为等待模式
                byte[] bytes = new byte[buffer.remaining()];
                buffer.get(bytes);
                String body = new String(bytes, "UTF-8");
            } 
        }
    }
}

上面的代码中,我们判断是否可以开始读(此事件是否准备好了),如果可以读,我们会新建一个Bytebuffer来保存读取到的数据。

4、将数据写入连接

最后一步是将响应消息写回客户端。Java NIO中也提供了同样简单易用的实现方法。正好与读取大致相同,对于写操作的ByteBuffer对象,我们需要logger它并发送到通道上:

代码语言:javascript
复制
while (true) {
    int i = selector.select();
    if (i == 0) continue;

    Set<SelectionKey> selectionKeys = selector.selectedKeys();
    Iterator<SelectionKey> iterator = selectionKeys.iterator();

    while (iterator.hasNext()) {
        SelectionKey key = iterator.next();
        if (key.isAcceptable()) {
            ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
            SocketChannel channel = serverChannel.accept();
            channel.configureBlocking(false);
            SelectionKey selectionKey = channel.register(selector, Selectionkey.OP_READ);
        } else if (key.isReadable()) {
            SocketChannel channel = (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int readBytes = channel.read(buffer);
            if (readBytes > 0) {
                buffer.flip(); // 将其设为等待模式
                byte[] bytes = new byte[buffer.remaining()];
                buffer.get(bytes);
                String body = new String(bytes, "UTF-8");

                ByteBuffer responseBuffer = ByteBuffer.wrap("response message".getBytes());
                channel.write(responseBuffer);
            } 
        }
    }
}

在编写具有可伸缩性的高性能异步服务器时,使用Java NIO异步I/O是非常必要的。不仅如此,Java NIO还提供了大量的特性,可轻松处理文件IO、内存映射以及基于信道的安全威胁等。摆脱阻塞式I/O模型,掌握Java NIO异步I/O编程能力,可以使你在高性能方面取得重大提升。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-12-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java学习网 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档