java中的NIO是什么?

本文深入解析Java NIO的原理与应用,对比传统IO,强调NIO面向块而非流的特点,详细阐述非阻塞IO的实现机制,包括Selector的使用及Channel配置。并通过代码示例展示客户端和服务端的非阻塞套接字编程。

    通常的NIO是指的NON Blocking IO, 非阻塞IO的缩写.

    java中的NIO, 官方释义是 New IO.  也有人说就是NON Blocking IO. 此处感觉无所谓.

    在java中的NIO编程与传统IO相比的区别是:

        传统IO是面向流的,而NIO是面向块的. 

    这块我得理解是, 都是渴了去接水, 传统IO是到水龙头那地儿张着嘴喝饱再走. 而NewIO是把水杯(Buffer缓冲区)放在水龙头下面,然后找个人(Selector)帮自己看着水有没有接满, 接满了告诉自己一声,收到哪个人的信号后NewIO再来拿杯子一饮而尽, 这样的好处就是接水的时间NewIO就可以忙自己的事情了.      此处如果没有Selector这个对象的话,NIO也是需要拿着杯子等着了,所以没有Selector的话也是阻塞的. 可以得出下面结论:

        在java中NIO编程可以是阻塞的, 也可以是非阻塞的.

    阻塞情况:  比如文件相关的File相关的Channel都是阻塞式的, 在读取文件时 只用到了NIO包下面的缓冲区 Buffer(一般用的是ByteBuffer,除了boolean类型的基础类型以及键值对等等都有相应的Buffer提供), 以及FileChannel. 线程走到这的时候依然是阻塞中的.  如果是套接字编程的话, 如果不引用Selector对象而只是用NIO包下面提供的ServerSocketChannel以及SocketChannel对象, 以及缓冲区对象 ByteBuffer的话其实线程也是阻塞的.

    非阻塞情况: 只有当在网路编程中(因为File相关的Channel不是实现的Selectable接口,也就没法用Selector去监听)才能实现非阻塞的编程. 这里需要注意,所有的在非阻塞式编程中用到的Channel都需要调用configureBlocking(false)方法来设置为非阻塞模式, 然后在Channel的register方法中注册Selector选择器的监听,这样就可以实现非阻塞的网络NIO编程了.

    另外NIO还有个特性就是能对缓冲区对象操作,具体的方法大概有以下几种:

    Buffer.flip()     重置当前读写游标position为0,同时将读写的结束位置limit放在游标重置之前的位置.(写入Channel(也就是读取Buffer)之前调用,这样读Buffer的时候就可以从头开始读到写入结束的位置了)

    Buffer.clear()  重置当前读写游标position为0,将读写结束位置limit放在缓冲区最后一位capacity处(重写Buffer之前调用,这样的游标再次写入会覆盖之前的数据,就当之前的数据不存在)

    Buffer.compact()  作用类似于clear()方法,但并不会重写之前的数据, 会把读写游标置于之前数据的limit之后,也就是用Buffer剩余空间做新的缓冲区, 适用于之前数据还需要用的情况,这时候可以配合mark()和reset()(?reset方法好像是,记不请了) 来读取之前的数据.

具体的Buffer使用可以参考这位大牛写的博客:  https://blog.youkuaiyun.com/u011381576/article/details/79876754

下面是我从视频中临摹来大牛的一段NIO实现非阻塞套接字编程的代码:

public class TestNonBlockingNIO {
    //客户端
    @Test
    public void client() throws IOException {
        //1.获取通道
        SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));

        //2.切换成非阻塞模式
        sChannel.configureBlocking(false);

        //3.分配指定大小的缓冲区
        ByteBuffer buf=ByteBuffer.allocate(1024);

        //4.发送数据给服务端
        Scanner scan= new Scanner(System.in);

        while(scan.hasNext()){
            String str=scan.next();
            buf.put((new Date().toString()+"\n"+str).getBytes());
            buf.flip();
            sChannel.write(buf);
            buf.clear();
        }



        //5.关闭通道
        sChannel.close();
    }

    //服务端
    @Test
    public void server() throws IOException {
        //1.获取通道
        ServerSocketChannel ssChannel=ServerSocketChannel.open();

        //2.切换成非阻塞模式
        ssChannel.configureBlocking(false);

        //3.绑定连接
        ssChannel.bind(new InetSocketAddress(9898));

        //4.获取选择器
        Selector selector = Selector.open();

        ssChannel.register(selector, SelectionKey.OP_ACCEPT);

        //6.轮询式的获取选择器上已经 "准备就绪" 的事件
        while (selector.select()>0){

            //7.获取当前选择器中所有注册的"选择键(已就绪的监听事件)"
            Iterator<SelectionKey> it=selector.selectedKeys().iterator();

            while (it.hasNext()){
                //8.获取准备就绪的事件
                SelectionKey sk=it.next();
                //9.判断具体是什么时间准备就绪
                if(sk.isAcceptable()){
                    //10.若"接受就绪",获取客户端连接
                    SocketChannel sChannel = ssChannel.accept();

                    //11.切换非阻塞模式
                    sChannel.configureBlocking(false);

                    //12.将该通道注册到选择器上
                    sChannel.register(selector,SelectionKey.OP_READ);
                }else if(sk.isReadable()){
                    //13.获取当前选择器上"读就绪"状态的通道
                    SocketChannel sChannel=(SocketChannel)sk.channel();

                    //14.读取数据
                    ByteBuffer buf=ByteBuffer.allocate(1024);

                    int len=0;
                    while((len=sChannel.read(buf))>0){
                        buf.flip();
                        System.out.println(new String(buf.array(),0,len));
                        buf.clear();
                    }
                }
                //15.取消选择器
                it.remove();
            }
        }
    }
}

 

<think>好的,我现在需要回答用户关于NIO的定义及其常用模块的问题。用户还提供了三个引用内容,我需要参考这些内容并结合之前的对话来构建回答。首先,我应该明确用户的需求:他们想了解NIO的基本定义以及Java中常用的NIO模块有哪些。 首先,回顾用户提供的引用内容。引用[1]提到NIO是同步非阻塞的I/O模型,用于高并发和大量连接处理。引用[2]区分了BIO、NIO和AIO,指出NIO是非阻塞的。引用[3]以Tomcat的Http11NioProtocol为例,说明NIO如何通过单线程管理多个连接,提高效率。这些信息可以帮助我构建定义部分。 接下来,我需要确定NIO的常用模块。Java NIO的主要组件包括Buffer、Channel、Selector、Charset等。根据之前的回答,用户可能希望了解每个模块的作用和它们之间的关系。例如,Buffer用于数据存储,Channel用于数据传输,Selector用于多路复用,Charset处理字符编码。 然后,我需要考虑如何结构化回答。用户之前的回答有清晰的层次结构,比如分点说明,使用示例代码,以及相关问题。我应该遵循类似的格式,确保回答结构清晰,符合用户的要求。 另外,用户之前的回答中有引用标识,例如[^1]、[^3]。我需要检查提供的引用内容,正确引用相关部分。例如,在定义NIO时,引用[1]和[3]可以作为支持。在提到Selector多路复用时,引用[3]中的Tomcat例子可以说明其实际应用。 还需要注意用户可能有的深层需求。例如,用户可能想知道为什么NIO比BIO高效,或者不同模块如何协同工作。因此,在解释每个模块时,可以简要说明其优势,比如Selector如何减少线程数量,提升性能。 最后,生成相关问题。根据用户之前的模式,问题应围绕NIO的进一步应用、与其他IO模型的对比,以及模块的具体使用场景。例如,询问Selector的工作原理,或者NIO和AIO的区别。 需要确保回答符合系统级指令:使用中文,数学表达式正确,引用适当,生成相关问题。同时避免使用Markdown,保持自然的口语化表达。检查是否有遗漏的模块或错误的信息,确保内容准确可靠。</think>### NIO的定义与核心模块解析 #### 一、NIO的定义 **NIO(Non-blocking I/O)** 是一种同步非阻塞的I/O模型,核心特点是**单线程可管理多个连接**,通过事件驱动机制实现高效资源利用。与传统的阻塞式I/O(BIO)不同,NIO允许线程在数据未就绪时处理其他任务,而非阻塞等待[^1][^3]。 **关键特性**: 1. **非阻塞**:线程不会因I/O操作未完成而阻塞,例如`SocketChannel.configureBlocking(false)`。 2. **多路复用**:通过`Selector`监控多个通道(Channel)的事件(如连接、读、写)。 3. **零拷贝**:支持通过`FileChannel.transferTo()`减少数据在内存中的复制次数。 --- #### 二、Java NIO的常用模块 ##### 1. **Buffer(缓冲区)** - **作用**:临时存储数据,提供结构化访问接口。 - **类型**:`ByteBuffer`、`IntBuffer`、`CharBuffer`等。 - **核心操作**: ```java ByteBuffer buffer = ByteBuffer.allocate(1024); // 分配空间 buffer.put(data); // 写入数据 buffer.flip(); // 切换为读模式 buffer.get(); // 读取数据 buffer.clear(); // 清空缓冲区 ``` ##### 2. **Channel(通道)** - **作用**:连接数据源(如文件、套接字),支持双向数据传输。 - **常见实现**: - `FileChannel`:文件读写。 - `SocketChannel`/`ServerSocketChannel`:TCP网络通信。 - `DatagramChannel`:UDP通信。 - **示例**: ```java // 文件复制(零拷贝) try (FileChannel src = FileChannel.open(Path.of("source.txt")); FileChannel dest = FileChannel.open(Path.of("dest.txt"), StandardOpenOption.CREATE)) { src.transferTo(0, src.size(), dest); } ``` ##### 3. **Selector(选择器)** - **作用**:实现多路复用,监听多个Channel的事件(如连接就绪、可读、可写)。 - **核心流程**: 1. 将Channel注册到Selector,并指定关注的事件。 2. 调用`Selector.select()`阻塞等待事件发生。 3. 通过`SelectionKey`处理就绪的Channel。 - **优势**:单线程可管理数千连接,减少线程切换开销[^3]。 ##### 4. **Charset(字符集)** - **作用**:处理字节与字符的编码/解码。 - **示例**: ```java Charset charset = Charset.forName("UTF-8"); CharBuffer charBuffer = charset.decode(byteBuffer); // 字节转字符 ByteBuffer byteBuffer = charset.encode("Hello"); // 字符转字节 ``` --- #### 三、NIO与BIO的性能对比 | **特性** | **BIO** | **NIO** | |----------------|----------------------------------|----------------------------------| | 线程模型 | 1连接1线程 | 单线程管理多连接 | | 阻塞行为 | 读写操作阻塞线程 | 仅Selector.select()可能阻塞 | | 适用场景 | 低并发、短连接 | 高并发、长连接(如聊天服务器) | | 资源消耗 | 线程数随连接数线性增长 | 固定少量线程 | --- #### 四、实际应用案例 以Tomcat的`Http11NioProtocol`为例: 1. **启动过程**:加载NIO组件,初始化线程池和Selector。 2. **请求处理**:Selector监听所有连接,当请求到达时分配线程处理。单线程可同时管理多个连接的HTTP请求与响应。 --- ### 相关问题 1. **NIO的Selector如何实现多路复用?** 2. **FileChannel的零拷贝技术如何提升文件传输效率?** 3. **NIO和AIO(异步I/O)的核心区别是什么?**
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值