前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面试官:Java Nio的优缺点?可能的瓶颈有哪些?

面试官:Java Nio的优缺点?可能的瓶颈有哪些?

作者头像
Java宝典
发布2023-08-30 13:14:39
4360
发布2023-08-30 13:14:39
举报

了解NIO

代码语言:javascript
复制
        Java NIO(New Input/Output)是Java提供的一种新的I/O(输入/输出)模型,引入了基于通道(Channel)和缓冲区(Buffer)的概念,相比传统的流式I/O模型,提供了更高效、更灵活的I/O操作方式。NIO中的 N 可以理解为 Non-blocking,同步非阻塞IO。

NIO 提供了与传统 BIO 模型中的 SocketServerSocket 相对应的 SocketChannelServerSocketChannel 两种不同的套接字通道实现。

NIO 这两种通道都支持阻塞和非阻塞两种模式。

阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式有较好的性能和可靠性

应用场景

Java NIO 提供了一种更高效、更灵活的方式来处理I/O操作

因此特别适用于高并发大规模数据处理的场景。它在网络编程文件处理多媒体处理等领域都有广泛的应用。

核心概念

Java NIO 的核心概念包括以下几个部分:

通道(Channel)

  • 通道是数据传输的载体,可以与文件或网络套接字建立连接。
  • 通道提供了双向的数据传输能力,可以读取和写入数据。
  • Java NIO 中的所有 I/O 操作都是通过通道进行的。

缓冲区(Buffer)

  • 缓冲区是一块连续的内存区域,用于存储数据。
  • 缓冲区提供了对数据的结构化访问,可以方便地读取和写入数据。
  • 数据通过缓冲区进行传输,从通道Channel读取到缓冲区,从缓冲区写入到通道Channel。

选择器(Selector)

  • 选择器是用于多路复用 I/O 的组件,可以通过一个线程管理多个通道。
  • 选择器可以同时监控多个通道上的事件,如读取事件、写入事件等。
  • 通过选择器,可以实现单线程处理多个通道的I/O操作。

Nio的常用方式

对于NIO来说selector主要用来接受客户端的连接,所以一般用在server端。我们以一个NIO的服务器端和客户端聊天室为例来讲解NIO在JDK中是怎么使用的。

我们选择Socket协议为基础的ServerSocketChannel

首先就是open这个Server channel:

代码语言:javascript
复制
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress("localhost", 9090));
serverSocketChannel.configureBlocking(false);

然后向server channel中注册selector:

代码语言:javascript
复制
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

虽然是NIO,但是对于Selector来说,它的select方法是阻塞方法,

只有找到匹配的channel之后才会返回,为了多次进行select操作,我们需要在一个while循环里面进行selector的select操作:

代码语言:javascript
复制
while (true) {
            selector.select();
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> iter = selectedKeys.iterator();
            while (iter.hasNext()) {
                SelectionKey selectionKey = iter.next();
                if (selectionKey.isAcceptable()) {
                    register(selector, serverSocketChannel);
                }
                if (selectionKey.isReadable()) {
                    serverResponse(byteBuffer, selectionKey);
                }
                iter.remove();
            }
            Thread.sleep(1000);
        }

selector中会有一些SelectionKey,SelectionKey中有一些表示操作状态的OP Status,根据这个OP Status的不同,selectionKey可以有四种状态,分别是isReadable,isWritable,isConnectable和isAcceptable。

当SelectionKey处于isAcceptable状态的时候,表示ServerSocketChannel可以接受连接了,我们需要调用register方法将serverSocketChannel accept生成的socketChannel注册到selector中,以监听它的OP READ状态,后续可以从中读取数据:

代码语言:javascript
复制
private static void register(Selector selector, ServerSocketChannel serverSocketChannel)
            throws IOException {
        SocketChannel socketChannel = serverSocketChannel.accept();
        socketChannel.configureBlocking(false);
        socketChannel.register(selector, SelectionKey.OP_READ);
    }

当selectionKey处于isReadable状态的时候,表示可以从socketChannel中读取数据然后进行处理:

代码语言:javascript
复制
private static void serverResponse(ByteBuffer byteBuffer, SelectionKey selectionKey)
            throws IOException {
        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
        socketChannel.read(byteBuffer);
        byteBuffer.flip();
        byte[] bytes= new byte[byteBuffer.limit()];
        byteBuffer.get(bytes);
        log.info(new String(bytes).trim());
        if(new String(bytes).trim().equals(BYE_BYE)){
            log.info("BYE");
            socketChannel.write(ByteBuffer.wrap("BYE".getBytes()));
            socketChannel.close();
        }else {
            socketChannel.write(ByteBuffer.wrap("BYE—BYE".getBytes()));
        }
        byteBuffer.clear();
    }

上面的serverResponse方法中,从selectionKey中拿到对应的SocketChannel,然后调用SocketChannel的read方法,将channel中的数据读取到byteBuffer中,要想回复消息到channel中,还是使用同一个socketChannel,然后调用write方法回写消息给client端,到这里一个简单的回写客户端消息的server端就完成了。

接下来就是对应的NIO客户端,在NIO客户端需要使用SocketChannel,首先建立和服务器的连接:

代码语言:javascript
复制
socketChannel = SocketChannel.open(new InetSocketAddress("localhost", 9090));

然后就可以使用这个channel来发送和接受消息了:

代码语言:javascript
复制
public String sendMessage(String msg) throws IOException {
        byteBuffer = ByteBuffer.wrap(msg.getBytes());
        String response = null;
        socketChannel.write(byteBuffer);
        byteBuffer.clear();
        socketChannel.read(byteBuffer);
        byteBuffer.flip();
        byte[] bytes= new byte[byteBuffer.limit()];
        byteBuffer.get(bytes);
        response =new String(bytes).trim();
        byteBuffer.clear();
        return response;
    }

向channel中写入消息可以使用write方法,从channel中读取消息可以使用read方法。

这样一个NIO的客户端就完成了。

虽然以上是NIO的server和client的基本使用,但是基本上涵盖了NIO的所有要点

优缺点及瓶颈

  • 优点
    1. 非阻塞:Java NIO使用了非阻塞I/O模型,允许应用程序在进行I/O操作时不必等待,可以继续处理其他任务,提高了系统的并发性能。
    2. 内存效率:Java NIO使用了直接内存缓冲区(Direct Buffer),可以直接操作系统内存,避免了数据在Java堆和操作系统之间的复制,提高了内存使用效率
    3. 多路复用:有selector多路复用,因此1个线程就行就能处理所有连接,这个线程不停循环遍历处理channel,可以通过一个线程同时监视多个通道的状态,从而实现单线程处理多个I/O操作。
  • 缺点
    1. 学习曲线:对于初学者来说,理解和使用Java NIO的概念和 API 可能需要一定的学习曲线,相对于传统的Java I/O,需要更多的学习和实践。
    2. 复杂性:相比传统的Java I/O,Java NIO的编程模型更加复杂,需要处理事件驱动和回调机制,编写的代码可能更加复杂和难以理解。
    3. 性能方面:单线程不停循环发起系统调用,一样会耗尽 CPU 资源。
  • NIO 可能的瓶颈
    1. 使用内存占用:使用直接内存缓冲区可能导致较高的内存消耗,特别是在处理大量数据时,对于内存的管控需要更加严格。
    2. 复杂性导致的错误:由于Java NIO的复杂性,编写代码时可能容易出错,例如处理错误的事件、资源泄漏等
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-07-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 java宝典 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 了解NIO
  • 应用场景
  • 核心概念
  • Nio的常用方式
  • 优缺点及瓶颈
相关产品与服务
多媒体处理
多媒体处理(Multimedia Processing,MMP)是数据万象推出的音视频处理服务,集成音视频转码、极速高清、精彩集锦、超分辨率、数字水印等能力,满足传媒、文旅、电商等各行业多媒体处理需求。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档