有哪些网站可以做店面设计,网站制作公司 哪家好,怎么把东西发布到网上卖,个人视频制作公司目录 NIO1、操作系统级别下的IO模型有哪些#xff1f;2、Java语言下的IO模型有哪些#xff1f;3、Java的NIO应用场景#xff1f;相比于IO的优势在哪#xff1f;4、Java的IO、NIO、AIO 操作文件读写5、NIO的核心类 :Buffer#xff08;缓冲区#xff09;、Channel#xff… 目录 NIO1、操作系统级别下的IO模型有哪些2、Java语言下的IO模型有哪些3、Java的NIO应用场景相比于IO的优势在哪4、Java的IO、NIO、AIO 操作文件读写5、NIO的核心类 :Buffer缓冲区、Channel通道、Selector选择器6、Java NIO中的零拷贝优化支持 NIO
1、操作系统级别下的IO模型有哪些
阻塞式 IO (Blocking IO): 当应用程序发起 IO 操作时如果数据还没有准备好或者无法立即处理IO 操作会阻塞程序的执行直到数据准备就绪或者操作完成为止。
非阻塞式 IO (Non-blocking IO): 在非阻塞 IO 模型中应用程序发起 IO 操作后会立即返回无论数据是否就绪或者能否立即处理。这样程序可以继续执行其他任务而不必等待 IO 操作完成。需要通过轮询或者事件通知等方式来检查 IO 操作的状态。
IO 复用 (IO Multiplexing): IO 复用模型通过操作系统提供的多路复用机制如 select、poll 或 epoll在一个线程中同时监视多个 IO 通道的状态。当其中任意一个 IO 通道就绪时程序可以进行相应的处理。常见于网络编程中。
信号驱动 IO (Signal-driven IO): 在信号驱动 IO 模型中应用程序会将 IO 操作请求发送给操作系统并注册一个信号处理函数。当 IO 操作完成时操作系统会发送一个信号给应用程序通知其 IO 操作已完成然后应用程序可以调用相应的处理函数来处理数据。
异步 IO (Asynchronous IO): 异步 IO 模型中应用程序发起 IO 操作后立即返回但是会指定一个回调函数或者事件处理器。当 IO 操作完成时操作系统会通知应用程序然后调用指定的回调函数来处理数据。相比非阻塞 IO异步 IO 不需要程序通过轮询来检查 IO 状态因此效率更高。
2、Java语言下的IO模型有哪些
BIO (Blocking I/O) BIO 属于同步阻塞 IO 模型该模型中应用程序发起IO操作后会一直阻塞直到操作系统内核把数据拷贝到用户空间。
NIO (Non-blocking I/O) Java 中的 NIO 于 JDK 1.4 中引入对应 java.nio 包提供了 Channel , SelectorBuffer 等抽象。 它是支持面向缓冲的基于通道的 I/O 操作方法。 NIO适用于 高负载、高并发的网络请求。 Java 中的 NIO 可以看作是 IO 复用模型
AIO (Asynchronous I/O)AIO JDK 1.7 中引入 它是异步 IO 模型。 异步 IO 是基于事件和回调机制实现的也就是应用操作之后会直接返回不会堵塞在那里当后台处理完成操作系统会通知相应的线程进行后续的操作。
3、Java的NIO应用场景相比于IO的优势在哪
多路复用 NIO可以使用单个线程管理多个通道这种多路复用的特性使得它非常适合处理大量的并发连接例如网络服务器。 NIO提供了选择器Selector机制可以通过一个线程管理多个通道的IO操作。当某个通道有IO事件发生时可以通过选择器进行通知从而实现高效的事件驱动模型。
非阻塞 I/O NIO 支持非阻塞 I/O这意味着在执行 I/O 操作时线程不会被阻塞。这使得在网络传输中可以有效地管理大量并发连接。
缓冲 NIO 读写数据都是通过缓冲区进行操作的。读操作的时候将 Channel 中的数据填充到 Buffer 中而写操作时将 Buffer 中的数据写入到 Channel 中。NIO利用 Buffer 和Channel可以高效的管理网络IO中的字节流数据。 这点类似于传统IO中的 BufferedInputStream中的缓冲区。
总的来说 NIO性能优势主要体现在处理高并发的网络IO场景。 传统 I/O 在网络通信中主要使用阻塞式 I/O为每个连接分配一个线程。当连接数量增加时系统性能将受到严重影响线程资源成为系统的性能瓶颈。而 NIO 提供了非阻塞 I/O 和 I/O 多路复用可以在单个线程中处理多个并发连接从而在网络传输中显著提高性能。
对于处理请求数较少或者少量连接读写大文件的场景其优势相对于传统IO并不明显。
4、Java的IO、NIO、AIO 操作文件读写 public static void main(String[] args) {/*传统IO*/try (BufferedWriter bufferedWriter new BufferedWriter(new FileWriter(123.txt))){bufferedWriter.write(测试传统IO);} catch (IOException e) {e.printStackTrace();}try (BufferedReader bufferedReader new BufferedReader(new FileReader(123.txt));){String line;while ((line bufferedReader.readLine()) ! null){System.out.println(line);}} catch (Exception e) {e.printStackTrace();}/*NIO*/Path path Paths.get(456.txt);try ( FileChannel fileChannel FileChannel.open(path, EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE))) {ByteBuffer buffer StandardCharsets.UTF_8.encode(测试NIO);fileChannel.write(buffer);} catch (IOException e) {e.printStackTrace();}try (FileChannel fileChannel FileChannel.open(path, StandardOpenOption.READ)){ByteBuffer buffer ByteBuffer.allocate(1024);int bytesRead fileChannel.read(buffer);while (bytesRead ! -1) {buffer.flip();System.out.println(StandardCharsets.UTF_8.decode(buffer));buffer.clear();bytesRead fileChannel.read(buffer);}} catch (IOException e) {e.printStackTrace();}/*AIO*/// 使用 Paths.get() 获取文件路径Path pathAIO Paths.get(789.txt);try {AsynchronousFileChannel fileChannel AsynchronousFileChannel.open(pathAIO, StandardOpenOption.WRITE, StandardOpenOption.CREATE);ByteBuffer buffer StandardCharsets.UTF_8.encode(测试AIO);FutureInteger result fileChannel.write(buffer, 0);result.get();fileChannel.close();} catch (Exception e) {e.printStackTrace();}try (AsynchronousFileChannel fileChannel AsynchronousFileChannel.open(pathAIO, StandardOpenOption.READ)){ByteBuffer buffer ByteBuffer.allocate(1024);// 异步读取 主线程会继续往下执行 为了防止读取完成之前 程序运行结束 使用线程同步器来处理同步问题CountDownLatch countDownLatch new CountDownLatch(1);fileChannel.read(buffer, 0, buffer, new CompletionHandler() {Overridepublic void completed(Integer result, ByteBuffer attachment) {attachment.flip();System.out.println(StandardCharsets.UTF_8.decode(attachment));attachment.clear();countDownLatch.countDown();}Overridepublic void failed(Throwable exc, ByteBuffer attachment) {System.out.println(读取失败);exc.printStackTrace();countDownLatch.countDown();}});// 等待异步操作完成countDownLatch.await();} catch (Exception e) {e.printStackTrace();}}5、NIO的核心类 :Buffer缓冲区、Channel通道、Selector选择器
在传统的BIO(Blocking IO)中 IO 是面向流的处理比如 InputStream和 OutputStream面向流的 I/O 系统一次一个字节地处理数据。 NIO(Non-blocking IO) 是面向块(缓冲区)的处理面向块(缓冲区)的 I/O 系统以缓存块的形式处理数据。 有点类似于BIO中的 缓冲流 BufferedInputStream和BufferedOutputStream
在NIO体系中是以 Buffer 缓冲区和 Channel 通道配合来处理数据的。
Buffer缓冲区 Buffer是抽象类 其中最常用的之类是 ByteBuffer 字节缓冲区。
Buffer中维护了4个重要的变量用来描述缓冲区的功能特性。
capacity 缓冲区能够容纳的数据元素的最大数量。容量在缓冲区创建时被设定不能被改变不能为负数。limit 缓冲区的限制 是第一个不应该读取或写入的元素的索引。缓冲区的限制不能为负并且不能大于其容量。 可以理解为Buffer 中可以读/写数据的边界。在写模式下limit 代表最多能写入的数据一般等于 capacity可以通过limit(int newLimit)方法设置读模式下limit 等于 Buffer 中实际写入的数据大小。position 下一个要被读或写的元素的索引。position 会自动由相应的 get()和 put()函数更新。缓冲区的位置不能为负并且不能大于其限制。 从写操作模式到读操作模式切换的时候调用flip方法position 都会归零这样就可以从头开始读写了。mark 标记Buffer允许将位置直接定位到该标记处这是一个可选属性
JDK文档中说明标记、位置、限制和容量值遵守以下关系 0 标记 位置 限制 容量
**Channel ** Channel 通道只负责传输数据、不直接操作数据。操作数据都是通过 Buffer 缓冲区来进行操作通常通道可以分为两大类(FileChannel)文件通道和(SocketChannel)套接字通道。
BIO 中的流是单向的分为各种 InputStream输入流和 OutputStream输出流数据只是在一个方向上传输类似于通信信道的单工通信方式。 通道与流的不同之处在于通道是双向的它可以用于读、写或者同时用于读写(类似于通信信道的全双工通信方式)。
常用的Channel实现 FileChannel用于文件 I/O 的通道支持文件的读、写和追加操作。FileChannel 允许在文件的任意位置进行数据传输支持文件锁定以及内存映射(涉及零拷贝优化相关技术)文件等高级功能。FileChannel 无法设置为非阻塞模式因此它只适用于阻塞式文件操作。 SocketChannel用于 TCP 套接字 I/O 的通道。SocketChannel 支持非阻塞模式可以与 Selector一起使用实现高效的网络通信。SocketChannel 允许连接到远程主机进行数据传输。 ServerSocketChannel用于监听 TCP 套接字连接的通道。与 SocketChannel 类似ServerSocketChannel 也支持非阻塞模式并可以与 Selector 一起使用。ServerSocketChannel 负责监听新的连接请求接收到连接请求后可以创建一个新的 SocketChannel 以处理数据传输。 DatagramChannel用于 UDP 套接字 I/O 的通道。DatagramChannel 支持非阻塞模式可以发送和接收数据报包适用于无连接的、不可靠的网络通信。 AsynchronousFileChannelAsynchronousFileChannel 是 Java 7 引入的一个异步文件通道类提供了对文件的异步读、写、打开和关闭等操作。
FileChannel的代码示例
public static void main(String[] args) {try (FileChannel sourceChannel FileChannel.open(Paths.get(C:\\123.txt), StandardOpenOption.READ);FileChannel destinationChannel FileChannel.open(Paths.get(C:\\123_copy.txt), StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {ByteBuffer buffer ByteBuffer.allocate(8*1024);// 如果还有数据就循环读取写入 while (sourceChannel.read(buffer) ! -1) {// 转换写模式buffer.flip();// 写入destinationChannel.write(buffer);// 写入后重置缓冲区buffer.clear();}}catch (Exception e){e.printStackTrace();}}AsynchronousFileChannel的代码示例
public static void main(String[] args){Path path Paths.get(123.txt);// 使用 Future 方式try (AsynchronousFileChannel fileChannel AsynchronousFileChannel.open(path, StandardOpenOption.READ)) {ByteBuffer buffer ByteBuffer.allocate(1024);long position 0;while (true) {FutureInteger result fileChannel.read(buffer, position);int bytesRead result.get();if (bytesRead 0) {break;}position bytesRead;// 转换成读模式buffer.flip();byte[] data new byte[buffer.limit()];buffer.get(data);System.out.println(new String(data));buffer.clear();}}catch (Exception e){e.printStackTrace();}// 实现 CompletionHandler 接口方式try (AsynchronousFileChannel fileChannel AsynchronousFileChannel.open(path, StandardOpenOption.READ);){ByteBuffer buffer ByteBuffer.allocate(1024);AtomicLong position new AtomicLong(0);CountDownLatch latch new CountDownLatch(1);fileChannel.read(buffer, position.get(), null, new CompletionHandlerInteger, Object() {Overridepublic void completed(Integer bytesRead, Object attachment) {if (bytesRead 0) {position.addAndGet(bytesRead);buffer.flip();byte[] data new byte[buffer.limit()];buffer.get(data);System.out.print(new String(data));buffer.clear();fileChannel.read(buffer, position.get(), attachment, this);} else {latch.countDown();}}Overridepublic void failed(Throwable exc, Object attachment) {System.out.println(Error: exc.getMessage());latch.countDown();}});latch.await();} catch (Exception e) {e.printStackTrace();}}Selector Selector选择器 是基于事件驱动的 I/O 多路复用模型它允许一个线程处理多个 Channel(这点非常重要)。 Selector 模型通过将 I/O 操作转化为事件驱动的方式实现了高效的多路复用来提高系统的并发处理能力和效率。
Selector的重要特性 事件注册 在 Selector 模型中程序会向 Selector 对象注册感兴趣的事件这些事件可以是连接Socket建立、读数据、写数据等。 也就是一个Selector 可以注册 多个Channel我们只需要使用一个线程管理Selector就能够处理Selector 上的多个通道Channel。 NIO处理高并发的关键所在。 事件监听 Selector 会不断地轮询注册在其上的通道Channel检查这些通道是否有已经就绪的事件发生。 事件处理 当 Selector 发现某个通道上发生了感兴趣的事件时它会通知程序并且程序可以根据事件类型执行相应的操作。例如如果一个通道的数据可读程序可以读取数据并进行处理如果一个通道的数据可写程序可以将数据写入通道。 非阻塞式调用 在 Selector 模型中通常会使用非阻塞式调用Non-blocking I/O这意味着程序可以在等待事件发生时继续执行其他任务而不会被阻塞。 多路复用 Selector 能够同时监听多个通道当任何一个通道上发生感兴趣的事件时Selector 都能及时地通知程序因此能够有效地实现多路复用提高系统的并发处理能力。 SelectionKey抽象类表示 Channel通道 在 Selector 中的注册信息以及与该通道相关联的状态和操作。
可以通过Selector 抽象类的 open方法 创建 Selector 实例
try {Selector selector Selector.open();} catch (IOException e) {e.printStackTrace();}在一个Selector 实例中 有三种 SelectionKey 集合分别用来返回不同状态的 Channel通道信息 分别对应Selector 中的三个方法
keys方法 返回的 SetSelectionKey 代表了注册在该 Selector 上的 Channel selectedKeys方法: 返回的 SetSelectionKey 代表了所有可通过 select() 方法获取的、需要进行 IO 处理的 Channel 已取消键集: 是已被取消但其通道尚未注销的键的集合。不可直接访问此集合。在下一次执行 select() 方法时这些 Channel 对应的 SelectionKey 会被彻底删除
SelectionKey 中定义了四种事件注册类型
public static final int OP_READ 1;
public static final int OP_WRITE 4;
public static final int OP_CONNECT 8;
public static final int OP_ACCEPT 16; OP_READ值为1表示通道已经准备好进行读取操作。当通道中有数据可读时将触发该事件。
OP_WRITE值为4表示通道已经准备好进行写入操作。当通道可写入数据时将触发该事件。
OP_CONNECT值为8表示通道已经完成连接操作。该事件通常在客户端套接字进行连接时使用。
OP_ACCEPT值为16表示服务器套接字已经准备好接受新的连接。该事件通常在服务器端套接字接受新连接时使用。
这些常量可以在 SelectionKey 的 interestOps() 方法中使用用于指定注册感兴趣的事件类型。当注册的事件发生时将触发相应的操作。Selector的简单代码示例
通过 ServerSocketChannel 实现群聊功能 服务端 import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;public class TestA {public static void main(String[] args) {try {// 创建ServerSocketChannel实例 接受客户端连接请求ServerSocketChannel serverSocketChannel ServerSocketChannel.open();// 设置为非阻塞模式// 当调用 accept() 方法时如果没有新的连接请求到达该方法将立即返回null而不是阻塞等待新的连接。// 这样可以使服务器同时处理多个连接而不必为每个连接创建一个新的线程serverSocketChannel.configureBlocking(false);// 绑定 18848端口serverSocketChannel.socket().bind(new InetSocketAddress(18848));// 创建Selector实例Selector selector Selector.open();// 注册ServerSocketChannel 到 Selector 监听 OP_ACCEPT 事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);// 通过死循环持续监听 Selector 内通道的事件while (true) {// 阻塞地监听通道是否有事件发生。如果有通道已经准备好的事件则 select() 方法会返回已经就绪的通道数int readyCounts selector.select();// 如果没有就绪状态的通道就继续下一轮循环if (readyCounts 0) {continue;}// 获取 selectedKeys 需要进行 IO 处理的 ChannelSetSelectionKey selectedKeys selector.selectedKeys();IteratorSelectionKey keyIterator selectedKeys.iterator();// 如果有就绪的通道 就循环处理while (keyIterator.hasNext()) {SelectionKey key keyIterator.next();if (key.isAcceptable()) {// 处理连接事件ServerSocketChannel server (ServerSocketChannel) key.channel();SocketChannel client server.accept();client.configureBlocking(false);// 将客户端通道注册到 Selector 并监听 OP_READ 事件client.register(selector, SelectionKey.OP_READ);} else if (key.isReadable()) {// 处理读事件SocketChannel client (SocketChannel) key.channel();ByteBuffer buffer ByteBuffer.allocate(1024);int bytesRead client.read(buffer);if (bytesRead 0) {buffer.flip();System.out.println(new String(buffer.array(), 0, bytesRead));// 将客户端通道注册到 Selector 并监听 OP_WRITE 事件client.register(selector, SelectionKey.OP_WRITE);} else if (bytesRead 0) {// 客户端断开连接client.close();}} else if (key.isWritable()) {// 处理写事件SocketChannel client (SocketChannel) key.channel();// 接收到客户端的数据后 反馈给客户端 发送成功ByteBuffer buffer ByteBuffer.wrap(发送成功.getBytes());client.write(buffer);// 将客户端通道注册到 Selector 并监听 OP_READ 事件client.register(selector, SelectionKey.OP_READ);}keyIterator.remove();}}} catch (IOException e) {e.printStackTrace();}}
}客户端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;public class TestB {private Selector selector;private SocketChannel socketChannel;private static final String HOST localhost;private static final int PORT 18848;public TestB() {try {selector Selector.open();socketChannel SocketChannel.open(new InetSocketAddress(HOST, PORT));socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_READ);System.out.println(连接到 HOST : PORT 群聊了);} catch (IOException e) {e.printStackTrace();}}public void start() {new Thread(() - {try {while (true) {if (selector.select() 0) {for (SelectionKey key : selector.selectedKeys()) {selector.selectedKeys().remove(key);if (key.isReadable()) {readMessage();}}}}} catch (IOException e) {e.printStackTrace();}}).start();try (BufferedReader reader new BufferedReader(new InputStreamReader(System.in))) {sendMessage(我是TestB);String input;while ((input reader.readLine()) ! null) {sendMessage(TestB:input);}} catch (IOException e) {e.printStackTrace();}}private void sendMessage(String message) throws IOException {if (message ! null !message.trim().isEmpty()) {ByteBuffer buffer ByteBuffer.wrap(message.getBytes());socketChannel.write(buffer);}}private void readMessage() throws IOException {ByteBuffer buffer ByteBuffer.allocate(1024);int read socketChannel.read(buffer);if (read 0) {buffer.flip();String msg new String(buffer.array(), 0, read);System.out.println(msg);}}public static void main(String[] args) {new TestB().start();}
}**java.nio.file.Files 类 ** Files 类是 Java NIONew I/O包的一部分提供了对文件操作的高级支持包括对文件通道、文件锁等的操作。 相比于BIO的 File 类 支持更复杂和更高级的文件操作。 JDK1.7引入。
下面介绍一些常用的方法
①、判断文件是否存在
// Path 为路径相关的接口抽象 ,
// LinkOption是一个枚举类型用于指定在处理文件时如何处理符号链接symbolic links。
//符号链接是指向另一个文件或目录的特殊文件类似于Unix系统中的软链接或Windows系统中的快捷方式。
// LinkOption提供了两个常量
// NOFOLLOW_LINKS表示在处理文件时不要跟踪符号链接。如果指定了此选项在对文件进行操作时将不会解析符号链接所指向的实际文件而是直接操作符号链接本身。
// FOLLOW_LINKS表示在处理文件时要跟踪符号链接。如果指定了此选项在对文件进行操作时会自动解析符号链接然后操作符号链接所指向的实际文件。
public static boolean exists(Path path, LinkOption... options)public static void main(String[] args) {Path path Paths.get(D:\\123.txt);// 默认 FOLLOW_LINKSboolean exists Files.exists(path);System.out.println(exists);}②、创建文件
// FileAttribute是文件属性或目录属性的抽象 常用实现有 PosixFileAttributes
// 例如可以使用 PosixFileAttributes 来创建具有特定权限的文件
public static Path createFile(Path path,FileAttribute?... attrs)throws IOException
public static void main(String[] args) {Path path Paths.get(D:\\1234.txt);try {Files.createFile(path);} catch (IOException e) {e.printStackTrace();}}③、创建目录 public static void main(String[] args) {Path path Paths.get(D:\\12345);try {Files.createDirectory(path);} catch (IOException e) {e.printStackTrace();}}④、删除文件 public static void main(String[] args) {Path path Paths.get(D:\\1234.txt);try {Files.delete(path);} catch (IOException e) {e.printStackTrace();}}⑤、复制文件
public static void main(String[] args) {Path source Paths.get(D:\\123.txt);Path target Paths.get(D:\\1234.txt);try {// StandardCopyOption 有三个属性// REPLACE_EXISTING表示如果目标文件已经存在则用源文件替换目标文件。//COPY_ATTRIBUTES表示在复制文件时也复制文件的属性。这些属性包括文件的元数据例如文件权限、最后修改时间等。如果不使用此选项目标文件将会获得系统默认的属性。//ATOMIC_MOVE表示使用原子性操作来移动文件。原子性操作是指在一个步骤内完成的操作要么全部成功要么全部失败没有中间状态。这个选项通常用于将文件从一个位置原子性地移动到另一个位置确保移动操作的完整性。Files.copy(source,target, StandardCopyOption.REPLACE_EXISTING);} catch (IOException e) {e.printStackTrace();}}⑥、移动文件
public static void main(String[] args) {Path source Paths.get(D:\\123.txt);Path target Paths.get(D:\\1234.txt);try {Files.move(source,target, StandardCopyOption.REPLACE_EXISTING);} catch (IOException e) {e.printStackTrace();}}⑦、读取文本文件
public static void main(String[] args) {Path path Paths.get(D:\\1234.txt);try {ListString list Files.readAllLines(path, StandardCharsets.UTF_8);list.forEach(System.out::println);} catch (IOException e) {e.printStackTrace();}}⑧、写入文本文件
public static void main(String[] args) {Path path Paths.get(D:\\1234.txt);try {// 追加模式// Files.write(path,list,StandardCharsets.UTF_8, StandardOpenOption.APPEND);ListString list Arrays.asList(123, 456);Files.write(path,list,StandardCharsets.UTF_8);} catch (IOException e) {e.printStackTrace();}}⑨、遍历
public class TestC {public static void main(String[] args) {Path path Paths.get(D:\\);try {Files.walkFileTree(path,new MyFileVisitor());} catch (IOException e) {e.printStackTrace();}}}class MyFileVisitor extends SimpleFileVisitorPath {Overridepublic FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {// 准备访问目录return super.preVisitDirectory(dir, attrs);}Overridepublic FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {// 访问文件System.out.println(file);return super.visitFile(file, attrs);}Overridepublic FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {// 访问文件失败return super.visitFileFailed(file, exc);}Overridepublic FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {// 正在访问目录System.out.println(dir);return super.postVisitDirectory(dir, exc);}
}⑩、利用重写visitFile方法查找文件
public class TestC {public static void main(String[] args) {// 查找D盘有没有 123.txt 文件Path path Paths.get(D:\\);try {MyFileVisitor myFileVisitor new MyFileVisitor(123.txt);Files.walkFileTree(path,myFileVisitor);System.out.println(myFileVisitor.searchFilePath);} catch (IOException e) {e.printStackTrace();}}}class MyFileVisitor extends SimpleFileVisitorPath {private String searchFileName;public Path searchFilePath;public MyFileVisitor() {}public MyFileVisitor(String searchFileName) {this.searchFileName searchFileName;}Overridepublic FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {String fileName file.getFileName().toString();if (fileName.equals(searchFileName)) {searchFilePath file;// 如果找到了 就终止return FileVisitResult.TERMINATE;}// 如果没找到继续查找return FileVisitResult.CONTINUE;}
}6、Java NIO中的零拷贝优化支持
零拷贝优化 推荐看几篇博客大致了解下https://zhuanlan.zhihu.com/p/83398714 https://blog.csdn.net/a745233700/article/details/122660332?spm1001.2014.3001.5506
零拷贝Zero-copy是一种计算机程序设计领域的优化技术旨在减少数据在内存之间复制的次数从而提升系统性能特别是对于大量数据的传输尤为重要。在传统的数据处理流程中数据从一个位置如磁盘读取到操作系统内核缓冲区再从内核缓冲区复制到用户空间的应用程序缓冲区然后在某些场景下如网络传输数据可能还需要从用户空间复制回内核空间的网络缓冲区以便发送。
零拷贝技术试图消除或最小化这些不必要的数据复制步骤具体方法有以下几种 用户空间直接访问User-Space Direct Access: 允许应用程序直接访问内核管理的内存例如通过内存映射mmap文件这样数据可以从磁盘直接加载到用户空间并用于网络传输无需先复制到内核空间。 写时复制Copy-on-Write): 在数据实际被修改前多个进程可以共享同一份数据的内存映射只有当数据需要修改时才会创建数据的副本。 共享内存Shared Memory: 多个进程可以直接访问同一块内存区域避免数据在进程间复制。 DMADirect Memory Access : 在硬件级别实现零拷贝允许外围设备如网卡直接读写内存而不需要CPU介入数据搬运常用于高速网络传输。
通过这些技术零拷贝能够显著减少CPU使用率降低内存带宽消耗提升数据处理速度尤其是在高负载的网络服务器、数据库和文件系统中效果明显。
JDK中对于零拷贝的支持 MappedByteBuffer 和 FileChannel 的transferTo()/transferFrom()方法
MappedByteBuffer的使用 MappedByteBuffer 用于表示一个内存映射文件即将文件的一部分或全部映射到内存中以便通过直接操作内存来实现对文件的读写。这种方式可以提高文件 I/O 的性能因为操作系统可以直接在内存和磁盘之间传输数据无需通过 Java 应用程序进行额外的数据拷贝。
MappedByteBuffer 是 NIO 基于内存映射mmap这种零拷⻉⽅式的提供的⼀种实现底层实际是调用了 Linux 内核的 mmap 系统调用。它可以将一个文件或者文件的一部分映射到内存中形成一个虚拟内存文件这样就可以直接操作内存中的数据而不需要通过系统调用来读写文件。 public static void main(String[] args) throws IOException {try (FileChannel sourceChannel FileChannel.open(Paths.get(D:\\123.txt), StandardOpenOption.READ);FileChannel destinationChannel FileChannel.open(Paths.get(D:\\123.txt), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.READ)) {long fileSize sourceChannel.size();MappedByteBuffer sourceMappedBuffer sourceChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize);MappedByteBuffer destinationMappedBuffer destinationChannel.map(FileChannel.MapMode.READ_WRITE, 0, fileSize);for (int i 0; i fileSize; i) {// 一个字节一个字节写byte b sourceMappedBuffer.get(i);destinationMappedBuffer.put(i, b);}}}transferTo()/transferFrom()方法 transferTo()/transferFrom()是 NIO 基于发送文件sendfile这种零拷贝方式的提供的一种实现底层实际是调用了 Linux 内核的 sendfile系统调用。它可以直接将文件数据从磁盘发送到网络而不需要经过用户空间的缓冲区。
public static void main(String[] args) {Path sourcePath Paths.get(123.txt);Path destinationPath Paths.get(123_copy.txt);try (FileChannel sourceChannel FileChannel.open(sourcePath, StandardOpenOption.READ);FileChannel destinationChannel FileChannel.open(destinationPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {long position 0;long count sourceChannel.size();// 循环传输直到所有字节都被传输// 因为缓冲区大小限制、通道的限制、网络限制、文件系统限制、操作系统限制等因素可能会导致 transferTo 无法传输count 大小的数据while (position count) {long transferred sourceChannel.transferTo(position, count - position, destinationChannel);position transferred;}} catch (IOException e) {e.printStackTrace();}}