IO与NIO的简单理解

IO与NIO的简单理解,第1张

IO与NIO的简单理解

主要参考:NIO学习文档这个大神写的非常好,通俗易懂!!!!!!!!
注意,“旧”的I/O包已经使用NIO重新实现过,即使我们不显式的使用NIO编程,也能从中受益。

一、NIO与IO对比记忆 IONIO面向流面向缓存区阻塞IO非阻塞IO选择器 面向流与面向缓存区
  • 面向流
    Java IO是面向流的,这意味着你一次从一个流中读取一个或多个字节。如何处理读取的字节由你决定。它们不会被缓存到任何地方。此外,不能 *** 作在数据流中来回移动。如果需要在从流读取的数据中来回移动,则需要先将其缓存到缓冲区中。
  • 面向缓冲区
    Java NIO的面向缓冲区的方法略有不同。数据被读入一个缓冲区,然后从这个缓冲区中进行处理。您可以根据 *** 作在缓冲区中来回移动。这在处理过程中提供了更多的灵活性。
阻塞与非阻塞
  • Java IO是阻塞的,一次连接对应一个线程,当进行流的读写时,该线程会一直阻塞至读写完毕
  • Java NIO是非阻塞的,当线程从通道中读取数据时,若此时没有可用数据,此时线程不会被阻塞,可以被其他资源继续使用,直到有可用数据可被读取。非阻塞写也是如此。
选择器

使用单线程控制选择器管理多个通道。

二、NIO

三个主要概念

通道

通道与流的对比

  • 通道可以读也可以写,流一般来说是单向的(只能读或者写,所以之前我们用流进行IO *** 作的时候需要分别创建一个输入流和一个输出流)。
  • 通道可以异步读写。
  • 通道总是基于缓冲区Buffer来读写。

通道的实现

  • FileChannel: 用于文件的数据读写
  • DatagramChannel: 用于UDP的数据读写
  • SocketChannel: 用于TCP的数据读写,一般是客户端实现
  • ServerSocketChannel: 允许我们监听TCP链接请求,每个请求会创建会一个SocketChannel,一般是服务器实现

    Java NIO:从Channels读数据到Buffers,从Buffers写数据到Channels
缓存
  • 容量
  • 位置
  • 限制
选择器

Selector是一个组件,它可以检查一个或多个Java NIO 通道 实例,并确定哪些通道准备好进行读取或写入等 *** 作
使用选择器可以让一个线程处理多个通道,减少线程切换的资源消耗和线程的管理

Java NIO:一个线程使用一个选择器来处理 3 个通道的

三、NIO的非阻塞服务器

非阻塞服务器需要定时检查是否传入数据,以及检查数据的完整性。服务器可能需要多次检查,直到收到一条或多条完整消息,所以是一个定时的查询。

同样,非阻塞服务器需要定时检查是否有数据要写入。如果是,服务器需要检查写入数据的完整性,避免数据被部分写入。

所以服务器的channel需要定时执行的三个检查器是:

  • 读管道,新连接数据的传入;
  • 检查数据完整性;
  • 写管道,检查它是否可以将任何传出的消息写到任何打开的连接上。
四、RandomAccessFile类:动态读取文件内容

需求:上传文件到抖音或者快手等发布渠道,当文件过大,上传时可能出现连接超时等网络问题,渠道官方一般推荐使用分片上传机制,降低大文件上传的失败频率。使用IO的RandomAccessFile完成需求。类似于一次NIO文件 *** 作,针对缓存区做 *** 作,http请求类似于一个文件上传通道。

RandomAccessFile支持对随机访问文件的读写。同时,RandomAccessFile支持“随机访问”的方式。局限的是它只能读写文件。

RandomAccessFile的一个重要使用场景就是网络请求中的多线程下载及断点续传。当分片上传中的某一片上传失败时,可以根据分片号和文件位移量的位置得到需要重新 *** 作的文件片段,进行重新上传,我使用的是@Retryable注解使用在分片上传的方法上。

@Retryable(maxAttempts = 5, backoff = @Backoff(value = 1000, multiplier = 1.5))

四种访问模式:

  • “r”
    只读
  • “rw”
    可读可写。如果文件不存在,则会尝试创建它。
  • “rws”
    可读可写,与“rw”一样,还要求每次对文件内容或元数据的更新都同步写入底层存储设备。
  • “rwd”
    可读可写,与“rw”一样,还要求每次对文件内容的更新都同步写入底层存储设备。

代码示例:

		// 只读模式
		RandomAccessFile raf = new RandomAccessFile(file, "r");
        try {
            long length = file.length();
            //分片号 从1开始
            int startOffset = 1;
            int byteCount;
            // 缓存区 取文件长度和每片大小中小的值作为缓存区的容量
            byte[] buff = new byte[(int) Math.min(SIZE, length)];
            boolean finish = false;
            while (!finish) {
                // 指针位置 从 0 开始,每次位移量 = 1 * 每片大小
                raf.seek((startOffset - 1) * SIZE);
                // 每片的字节数
                byteCount = raf.read(buff);
                // 当本次上传的字节数未到达需要分片的大小 | 指针已经到末尾 证明是最后一片
                if (byteCount < SIZE || (startOffset) * SIZE == length) {
                    byte[] realChunkData = new byte[byteCount];
                    System.arraycopy(buff, 0, realChunkData, 0, byteCount);
                    buff = realChunkData;
                    finish = true;
                }
                //分片上传 nio:通过Channel管道运输着存储数据的Buffer缓冲区的来实现数据的处理
                this.uploadVideoShard(startOffset, buff);
                // 修改指针位置 未结束每次 + 1
                startOffset = finish ? startOffset : startOffset + 1;
                //分片上传完成 开始文件分片合片
                if (finish) {
                    JSONObject result = restTemplate.postForObject(completeUrl, HttpEntity.EMPTY, JSONObject.class);
                }
            }
        } finally {
            if (file.exists()) {
                file.delete();
            }
            // 关闭文件
            raf.close();
        }

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5693403.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存