NIO 类的基本用法 --《JAVA编程思想》79

Java NIO 深入理解:通道与缓冲器提升I/O性能

JDK 1.4 引入了 java.nio.* 新类库提升 I/O 读写速度, 效率的提升主要得益于 nio 类库中采用的数据结构更接近于操作系统执行 I/O 的方式:通道缓冲器

通道作为数据仓库,缓冲器作为搬运工,在通道内对数据进行读写操作,我们只操作缓冲器,不直接与通道产生联系。

1.创建通道

下面先来看个简单的示例:

FileOutputStream、RandomAccessFile、FileInputStream 分别对应只写通道读写通道读通道的获取方式;

FileChannel 中只能写入字节,读取的时候也是如此,无法直接读取字符;

需要注意的是,调用 FileChannel 中的 read() 方法后,必须调用 flip() 方法将 ByteBuffer 中的指针移动至头部(read() 方法调用后,指针会移动至尾部),否则无法读取出数据;

allocate() 用于创建指定大小的缓冲区,单位为字节;

使用通道缓冲器进行读写的效率会显著高于普通的 I/O 类,感兴趣的朋友可以自行测试;

public class GetChannel {

    public static void main(String[] args) throws IOException {
        //获取通道
        FileChannel fc = new FileOutputStream("D:\\test\\a.txt").getChannel();
        //缓冲器写入
        fc.write(ByteBuffer.wrap("some txt ".getBytes()));
        fc.close();
        fc = new RandomAccessFile("D:\\test\\a.txt", "rw").getChannel();
        //指定写入起始位置
        fc.position(fc.size());
        fc.write(ByteBuffer.wrap("some more ".getBytes()));
        fc.close();
        fc = new FileInputStream("D:\\test\\a.txt").getChannel();
        //创建指定大小的缓冲器
        ByteBuffer buff = ByteBuffer.allocate(1024);
        fc.read(buff);
        //必须执行,将缓冲区的指针移动至头部
        buff.flip();
        //使用迭代器遍历,类似Iterator的hasNext()
        while (buff.hasRemaining()) System.out.print((char) buff.get());
    }
    
}
some txt some more 

2.快速复制

我们可以将一个通道与另外一个通道直接相连,从而达到快速复制的效果,具体操作如下:

    public static void main(String[] args) throws IOException {
        FileChannel in = new FileInputStream("D:\\test\\a.txt").getChannel(),
                out = new FileOutputStream("D:\\test\\b.txt").getChannel();
        in.transferTo(0,in.size(),out);
        //第二种方式
        //out.transferFrom(in, 0, in.size());
    }

in.transferTo() 的参数分别为:读取的起始位置、读取的长度、写入的通道;

也可以通过 out.transferFrom() 来实现相同的效果,其对应的参数为:读取的通道、读取的起始位置、读取的长度

3.编码解码

缓冲器内容纳的是普通字节,为了将它们转换成字符,需要在输入它们的时候进行编码,或者输出的时候进行解码,否则读取出来的字符会出现乱码现象;

3.1 编码

通过 ByteBuffer 写入的时候,指定字节数组的编码格式;

  • 示例:
    public static void main(String[] args) throws IOException {
        FileChannel out = new FileOutputStream("D:\\test\\a.txt").getChannel();
        //指定写入字节的编码格式
        out.write(ByteBuffer.wrap("富强 民主 文明 和谐 自由 平等 公正 法治 爱国 敬业 诚信 友善".getBytes("UTF-16BE")));
        out.close();
        FileChannel in = new FileInputStream("D:\\test\\a.txt").getChannel();
        ByteBuffer buff = ByteBuffer.allocate(1024);
        in.read(buff);
        buff.flip();
        System.out.println(buff.asCharBuffer());
    }
富强 民主 文明 和谐 自由 平等 公正 法治 爱国 敬业 诚信 友善

3.2 解码

ByteBuffer 读取完成后,通过 Charset 类提供的方法对其进行解码操作;

  • 示例:
    public static void main(String[] args) throws IOException {
        FileChannel out = new FileOutputStream("D:\\test\\a.txt").getChannel();
        out.write(ByteBuffer.wrap("富强 民主 文明 和谐 自由 平等 公正 法治 爱国 敬业 诚信 友善".getBytes()));
        out.close();
        FileChannel in = new FileInputStream("D:\\test\\a.txt").getChannel();
        ByteBuffer buff = ByteBuffer.allocate(1024);
        in.read(buff);
        buff.flip();
        //对缓冲器内的文本进行解码
        System.out.println(Charset.forName("UTF-8").decode(buff));
    }
富强 民主 文明 和谐 自由 平等 公正 法治 爱国 敬业 诚信 友善

4.获取基本类型

ByteBuffer 虽然只能保存字节类型的数据,但是它提供读取和写入各种基本类型的方法。

  • 示例:
    ByteBuffer 提供 asCharBuffer()、asShortBuffer()、asIntBuffer() 等获取对应基本类型的视图,然后使用视图的 put() 方法插入数据,值得一提的是:ShortBuffer 的 put() 方法放入超过 short 最大上限的数值时,会进行窄化转换,导致结果会变更。

ByteBuffer.allocate(int capacity) 创建指定大小的缓冲区,并为每个位置的值自动置零,故可以通过判断当前位置是否为零得出其位置上有没有分配内容;

    public static void main(String[] args) throws IOException {
        //分配ByteBuffer的大小
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //获取char类型的视图
        CharBuffer charBuffer = buffer.asCharBuffer();
        charBuffer.put("put char");
        char c;
        while ((c = buffer.getChar()) != 0) {
            System.out.print(c);
        }
        //重置读取位置,从0开始
        buffer.rewind();
        System.out.println();
        //获取short类型的视图
        ShortBuffer shortBuffer = buffer.asShortBuffer();
        short max = Short.MAX_VALUE;
        System.out.println("short max:" + max);
        shortBuffer.put((short) 471124);
        System.out.println(buffer.getShort());
        shortBuffer.put((short) 1234);
        System.out.println(buffer.getShort());
        buffer.rewind();
        //获取int类型的视图
        IntBuffer intBuffer = buffer.asIntBuffer();
        intBuffer.put(99471142);
        System.out.println(buffer.getInt());
        buffer.rewind();
    }
put char
short max:32767
12372
1234
99471142

本次分享至此结束,希望本文对你有所帮助,若能点亮下方的点赞按钮,在下感激不尽,谢谢您的【精神支持】。

若有任何疑问,也欢迎与我交流,若存在不足之处,也欢迎各位指正!

<think>首先,用户的问题是:“我想了解Java NIO编程思想 请问Java NIO编程思想介绍”。用户明确要求介绍Java NIO编程思想。 回顾系统级指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - 使用中文回答。 - 生成相关问题放在末尾。 - 回答中引用的段落末尾自然地添加引用标识,例如[^1]。参考给定的引用,我需要基于这些引用内容来构建回答。 用户提供的引用: - 引用[1]:描述了Java NIO基本概念,包括非阻塞I/O、多路复用I/O和缓冲区机制。 - 引用[2]:讨论了未来发展趋势和挑战。 - 引用[3]:介绍了Java NIO的原理和特性。 - 引用[4]:提到了并发编程的结合应用、工具资源和未来趋势。 用户上一次的问题和回答:在系统提示中,用户之前的交互是“快速排序”的示例,但当前对话是关于Java NIO的,所以我需要专注于当前查询。 结构化回答: 1. **介绍部分**:简要解释Java NIO编程思想。 2. **核心思想详解**:基于引用,描述非阻塞I/O、多路复用、缓冲区等关键概念。 3. **优势和应用**:讨论为什么使用NIO,以及它的好处。 4. **引用相关部分**:在适当的地方添加引用标识。 5. **生成相关问题**:在末尾添加相关问题。 关键点从引用中提取: - 引用[1]:NIO的核心是**非阻塞I/O、多路复用 I/O 和缓冲区机制**- 引用[4]:提到了**通道(Channel)、缓冲区(Buffer)、选择器(Selector)** 作为核心组件。 - 编程思想强调高效处理并发连接,避免阻塞。 完整回答结构: - 开头:定义Java NIO编程思想- 主体: - 非阻塞I/O思想:解释与传统阻塞I/O的区别。 - 多路复用:使用选择器监控多个通道。 - 缓冲区机制:数据在缓冲区内处理。 - 事件驱动:响应事件而非轮询。 - 结尾:总结思想的好处。 - 相关问题:生成3-5个相关问题。 使用中文回答。 确保LaTeX语法正确:由于Java NIO涉及网络编程,可能没有太多数学公式,但如果有涉及计算的地方,使用指定格式。 引用标识: - 当提到核心概念时,引用[1]。 - 当提到发展趋势时,引用[2]或[4]。 - 例如,“核心在于非阻塞I/O、多路复用I/O和缓冲区机制[^1]。” 生成相关问题: - 基于主题,如:与非阻塞相关、应用场景、挑战等。 - 例如: 1. Java NIO与传统I/O的主要区别是什么? 2. 如何使用Java NIO实现高效网络服务器? 3. Java NIO在现实项目中的应用案例有哪些? 4. Java NIO的常见挑战和优化方法? 最终回答输出: - 清晰分段。 - 末尾添加“相关问题”。 </think>### Java NIO编程思想介绍 Java NIO(Non-blocking I/O)的编程思想主要体现在其核心设计上,旨在通过高效的异步处理机制解决传统阻塞I/O的性能瓶颈,尤其在高并发网络应用中提供更高的可扩展性和吞吐量。其核心思想可以概括为:**事件驱动、非阻塞操作、多路复用和缓冲区管理**。下面我将逐步解析这些关键思想。 #### 1. **事件驱动与多路复用** Java NIO的核心是使用**选择器(Selector)** 机制实现多路复用I/O。选择器允许单个线程监控多个通道(Channel)(如Socket通道或文件通道),当某个通道发生可读、可写或连接等事件时,选择器会通知线程进行处理,而不是让线程阻塞等待。这避免了传统阻塞I/O中每个连接都需要一个独立线程的开销,显著减少了线程上下文切换和资源消耗。 例如,在高并发服务器中,一个选择器线程可以处理数千个连接事件,实现“单线程管理多连接”的高效模式[^1]。 #### 2. **非阻塞操作** 与传统Java I/O(BIO)不同,Java NIO采用**非阻塞I/O模型**。在BIO中,读取或写入操作会阻塞线程直到数据就绪,导致线程空闲浪费;而NIO的通道操作是非阻塞的——如果数据未就绪,线程立即返回,继续执行其他任务。这提升了程序的响应速度和资源利用率,特别适合高延迟网络环境(如Web服务器处理大量并发请求)[^1][^3]。 数学上,非阻塞模型的效率可以通过资源利用率公式解释:设线程数为 $ n $,连接数为 $ m $,则传统阻塞模型的线程开销为 $ O(m) $,而NIO模型可优化到近似 $ O(1) $(仅需少量线程),这在高并发时优势明显。 #### 3. **缓冲区机制** Java NIO引入**缓冲区(Buffer)** 作为数据传输的统一容器。所有I/O操作都通过缓冲区进行:数据从通道读取到缓冲区内,或从缓冲区写入通道。缓冲区提供直接内存访问(如DirectBuffer),减少数据在JVM堆和操作系统内核间的拷贝次数,提升I/O性能。 这种机制的思想是“批量处理数据”,而非逐字节操作。例如,在网络传输中,数据可以预加载到缓冲区,程序只需关注缓冲区的状态(如position和limit),简化了数据管理[^1][^4]。 #### 4. **整体编程范式** Java NIO编程思想本质上是一种**反应式架构**:程序基于事件回调(如SelectionKey)驱动,而非主动轮询。开发者只需定义事件处理器(例如处理读/写事件),系统自动在事件触发时执行。这降低了资源争用,提升了可扩展性。但这也增加了编程复杂性,因为需要处理异步逻辑和线程同步问题(如避免缓冲区竞争)[^2][^4]。 总结来说,Java NIO编程思想**以高效并发为目标,通过非阻塞和多路复用减少等待时间,配合缓冲区优化数据传输**,最终实现高性能网络应用。 ### 优势与挑战 - **优势**:在高并发场景下(如聊天服务器或API网关),Java NIO可大幅提升吞吐量和响应速度,同时减少内存占用。 - **挑战**:编程模型较复杂,需处理事件循环和线程安全问题;但随着Netty等框架的发展,这些复杂性已被简化[^3][^4]。 未来,Java NIO可能进一步与虚拟线程(Project Loom)等技术融合,提供更简单的异步编程模型[^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BaymaxCS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值