当前位置: 首页 > news >正文

Java的NIO体系

目录

  • NIO
    • 1、操作系统级别下的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 , Selector,Buffer 等抽象。
它是支持面向缓冲的,基于通道的 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");Future<Integer> 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 支持非阻塞模式,可以发送和接收数据报包,适用于无连接的、不可靠的网络通信。

  • AsynchronousFileChannel:AsynchronousFileChannel 是 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) {Future<Integer> 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 CompletionHandler<Integer, 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方法: 返回的 Set<SelectionKey> 代表了注册在该 Selector 上的 Channel
    在这里插入图片描述
  • selectedKeys方法: 返回的 Set<SelectionKey> 代表了所有可通过 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):表示服务器套接字已经准备好接受新的连接。该事件通常在服务器端套接字接受新连接时使用。
这些常量可以在 SelectionKeyinterestOps() 方法中使用,用于指定注册感兴趣的事件类型。当注册的事件发生时,将触发相应的操作。

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 处理的 ChannelSet<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> 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 NIO(New 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 {List<String> 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);List<String> 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 SimpleFileVisitor<Path> {@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 SimpleFileVisitor<Path> {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?spm=1001.2014.3001.5506

零拷贝(Zero-copy)是一种计算机程序设计领域的优化技术,旨在减少数据在内存之间复制的次数,从而提升系统性能,特别是对于大量数据的传输尤为重要。在传统的数据处理流程中,数据从一个位置(如磁盘)读取到操作系统内核缓冲区,再从内核缓冲区复制到用户空间的应用程序缓冲区,然后在某些场景下(如网络传输),数据可能还需要从用户空间复制回内核空间的网络缓冲区以便发送。

零拷贝技术试图消除或最小化这些不必要的数据复制步骤,具体方法有以下几种:

  • 用户空间直接访问(User-Space Direct Access): 允许应用程序直接访问内核管理的内存,例如通过内存映射(mmap)文件,这样数据可以从磁盘直接加载到用户空间并用于网络传输,无需先复制到内核空间。

  • 写时复制(Copy-on-Write): 在数据实际被修改前,多个进程可以共享同一份数据的内存映射,只有当数据需要修改时才会创建数据的副本。

  • 共享内存(Shared Memory): 多个进程可以直接访问同一块内存区域,避免数据在进程间复制。

  • DMA(Direct 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();}}

相关文章:

Java的NIO体系

目录 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…...

自下而上的选股与自上而下的选股

一起学习了《战胜华尔街》&#xff0c;不知道大家有没有这么一种感受&#xff1a;林奇的选股方法是典型的自下而上的选股方法。虽然这一点没有单独拎出来讨论过&#xff0c;但在《从低迷中寻找卓越》《如何通过财务指标筛选股票&#xff1f;》《边逛街边选股&#xff1f;》《好…...

Tech Talk:智能电视eMMC存储的五问五答

智能电视作为搭载操作系统的综合影音载体&#xff0c;以稳步扩大的市场规模走入越来越多的家庭&#xff0c;成为人们生活娱乐的重要组成部分。存储部件是智能电视不可或缺的组成部分&#xff0c;用于保存操作系统、应用程序、多媒体文件和用户数据等信息。智能电视使用eMMC作为…...

scikit-learn教程

scikit-learn&#xff08;通常简称为sklearn&#xff09;是Python中最受欢迎的机器学习库之一&#xff0c;它提供了各种监督和非监督学习算法的实现。下面是一个基本的教程&#xff0c;涵盖如何使用sklearn进行数据预处理、模型训练和评估。 1. 安装和导入包 首先确保安装了…...

CentOS 7 搭建rsyslog日志服务器

CentOS 7 搭建rsyslog日志服务器 前言一、IP地址及主机名称规划1.修改主机名 二、配置rsyslog日志服务器1.安装rsyslog服务2.编辑/etc/rsyslog.conf 文件3.启动并启用rsyslog服务4.验证端口是否侦听 三、在rsyslog日志服务器上配置firewalld防火墙四、配置rsyslog日志客户端1.编…...

使用Spring Boot Actuator监控应用健康状态

使用Spring Boot Actuator监控应用健康状态 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们将探讨如何利用Spring Boot Actuator来监控和管理应用程序的…...

leetcode刷题:vector刷题

​ ​ &#x1f525;个人主页&#xff1a;guoguoqiang. &#x1f525;专栏&#xff1a;leetcode刷题 1.只出现一次的数字 这道题很简单&#xff0c;我们只需要遍历一次数组即可通过异或运算实现。(一个数与自身异或结果为0&#xff0c;任何数与0异或还是它本身) class Solut…...

CGI面试题及参考答案

什么是CGI?它在Web服务器与应用程序之间扮演什么角色? CGI(Common Gateway Interface) 是一种标准协议,它定义了Web服务器与运行在服务器上的外部程序(通常是脚本或应用程序)之间的通信方式。简单来说,CGI充当了一个桥梁,使得Web服务器能够将用户的请求传递给后端程序…...

论文调研_物联网漏洞检测综述

A Review of IoT Firmware Vulnerabilities and Auditing Techniques 研究背景&#xff1a;物联网设备在工业、消费类等各个领域得到了广泛应用&#xff0c;实现了更高的自动化和生产率。然而&#xff0c;这些连网设备的高度依赖也带来了一系列网络安全威胁&#xff0c;特别是…...

Java学习【IO流:深入理解与应用(上)】

Java学习【IO流&#xff1a;深入理解与应用&#xff08;上&#xff09;】 &#x1f343;1.IO流体系结构&#x1f343;2.FileOutputStream&#x1f341;2.1FileOutputStream写数据的三种方式&#x1f341;2.2换行和续写 &#x1f343;3.FileInputStream&#x1f341;3.1每次读取…...

干货系列:SpringBoot3第三方接口调用10种方式

环境&#xff1a;SpringBoot.3.3.0 1、简介 在项目中调用第三方接口是日常开发中非常常见的。调用方式的选择通常遵循公司既定的技术栈和架构规范&#xff0c;以确保项目的一致性和可维护性。无论是RESTful API调用、Feign声明式HTTP客户端、Apache HttpClient等调用方式&…...

KVM性能优化之CPU优化

1、查看kvm虚拟机vCPU的QEMU线程 ps -eLo ruser,pid,ppid,lwp,psr,args |awk /^qemu/{print $1,$2,$3,$4,$5,$6,$8} 注:vcpu是不同的线程&#xff0c;而不同的线程是跑在不同的cpu上&#xff0c;一般情况&#xff0c;虚拟机在运行时自身会点用3个cpus&#xff0c;为保证生产环…...

lua中判断2个表是否相等

当我们获取 table 长度的时候无论是使用 # 还是 table.getn 其都会在索引中断的地方停止计数&#xff0c;而导致无法正确取得 table 的长度&#xff0c;而且还会出现奇怪的现象。例如&#xff1a;t里面有3个元素&#xff0c;但是因为最后一个下表是5和4&#xff0c;却表现出不一…...

uni-app 自定义支付密码键盘

1.新建组件 payKeyboard .vue <template><view class"page-total" v-show"isShow"><view class"key-list"><view class"list" v-for"(item,index) in keyList" :class"{special:item.keyCode190…...

抖音微短剧小程序源码搭建:实现巨量广告数据高效回传

在数字化营销日益盛行的今天&#xff0c;抖音微短剧小程序已成为品牌与观众互动的新渠道。这些短小精悍的剧目不仅能迅速抓住用户的注意力&#xff0c;还能有效提升品牌的知名度和用户黏性。然而&#xff0c;想要充分利用这一营销工具&#xff0c;关键在于如何高效地追踪广告数…...

springboot数字化医院产科系统源码

目录 一、系统概述 二、开发环境 三、功能设计 四、功能介绍 一、系统概述 数字化产科是为医院产科量身定制的信息管理系统。它管理了孕妇从怀孕开始到生产结束42天一系列医院保健服务信息。该系统由门诊系统、住院系统、数据统计模块三部分组成&#xff0c;与医院HIS、LI…...

uniapp微信接口回调 response.sendRedirect nginx 报404错误

如题 参考 uniapp打包H5时,访问index.html页面白屏报错net::ERR_ABORTED 404 - 简书 nginx中修改 配置文件 location / { try_files $uri $uri/ /index.html; root html; index index.html index.htm; } uniapp里配置 重新载入...

Python系统教程02

巩固 input()输出函数 回顾 1 、 input()函数&#xff1a; 在 input()函数输入时&#xff0c;输入的内容一定为字符串类型。 2 、条件分支语句&#xff1a; 每一个 if 语句可以看成一个个体&#xff0c;elif 和 else 都是一个 if 个体的一部分&#xff0c;每一个 if 个体 运…...

JS面试题6——深拷贝和浅拷贝

它们都是用来复制的 1. 浅拷贝&#xff08;只复制引用&#xff0c;而未复制真正的值&#xff09; /* 简单赋值 */ var arr1 [a, b, c, d]; var arr2 arr1; /* Object.assign实现的也是浅拷贝 */ var obj1 {a:1, b:2} var obj2 Object.assign(obj1); 2. 深拷贝&#xff08;是…...

Scrapy实现关键词搜索的数据爬取

爬虫技术对于从互联网上获取数据和信息非常重要&#xff0c;而scrapy作为一款高效、灵活和可扩展的网络爬虫框架&#xff0c;能够简化数据爬取的过程&#xff0c;对于从互联网上爬取数据的工作非常实用。本文将介绍如何使用scrapy实现关键词搜索的数据爬取。 Scrapy的介绍 Sc…...

【Linux】ip命令详解

Linux中的ip命令是一个功能强大的网络配置工具,用于显示或操作路由、网络设备、策略路由和隧道。以下是关于ip命令的详细解释: 一、ip命令介绍 简介:ip命令是一个用于显示或操作路由、网络设备、策略路由和隧道的Linux命令行工具。它取代了早期的ifconfig命令,并提供了更多…...

软降工程学系统实现

一、程序编码 程序编码是设计的继续&#xff0c;将软件设计的结果翻译成用某种程序设计语言描述的源代码。 程序编码涉及到方法、工具和过程。 程序设计风格和程序设计语言的特性会深刻地影响软件的质量和可维护性。 要求源程序具有良好的结构性和设计风格。 程序设计风格…...

001 SpringMVC介绍

文章目录 基础概念介绍BS和CS开发架构应用系统三层架构MVC设计模式 SpringMVC介绍SpringMVC是什么SpringMVC与Spring的联系为什么要学习SpringMVC 六大组件介绍六大组件(MVC组件其他三大组件)说明 基础概念介绍 BS和CS开发架构 一种是C/S架构&#xff0c;也就是客户端/服务器…...

深入解析scikit-learn中的交叉验证方法

交叉验证是机器学习中用于评估模型性能的重要技术&#xff0c;它可以帮助我们理解模型在未知数据上的泛化能力。scikit-learn&#xff08;简称sklearn&#xff09;是一个广泛使用的Python机器学习库&#xff0c;提供了多种交叉验证方法。本文将详细介绍scikit-learn中提供的交叉…...

分布式kettle调度管理平台简介

介绍 Kettle&#xff08;也称为Pentaho Data Integration&#xff09;是一款开源的ETL&#xff08;Extract, Transform, Load&#xff09;工具&#xff0c;由Pentaho&#xff08;现为Hitachi Vantara&#xff09;开发和维护。它提供了一套强大的数据集成和转换功能&#xff0c…...

002-基于Sklearn的机器学习入门:基本概念

本节将继续介绍与机器学习有关的一些基本概念&#xff0c;包括机器学习的分类&#xff0c;性能指标等。同样&#xff0c;如果你对本节内容很熟悉&#xff0c;可直接跳过。 2.1 机器学习概述 2.1.1 什么是机器学习 常见的监督学习方法 2.1.2 机器学习的分类 机器学习一般包括监…...

ubuntu 默认的PATH配置

ubuntu 默认的PATH配置 在Ubuntu系统中&#xff0c;PATH环境变量是非常关键的&#xff0c;因为它定义了操作系统在接收到用户输入命令时&#xff0c;搜索可执行文件的目录顺序。这个变量的配置决定了哪些命令可以被系统全局识别和执行。 默认的PATH配置 Ubuntu的默认PATH环境…...

JAVA妇产科专科电子病历系统源码,前端框架:Vue,ElementUI

JAVA妇产科专科电子病历系统源码&#xff0c;前端框架&#xff1a;Vue&#xff0c;ElementUI孕产妇健康管理信息管理系统是一种将孕产妇健康管理信息进行集中管理和存储的系统。通过建立该系统&#xff0c;有助于提高孕产妇健康管理的效率和质量&#xff0c;减少医疗事故发生的…...

代码随想录算法训练营Day56|所有可达路径、797.所有可能的路径

所有可达路径 98. 所有可达路径 (kamacoder.com) 深度优先搜索&#xff0c;和之前的回溯题类似。 #include <iostream> #include <vector> using namespace std;// 定义一个二维向量来存储所有可能的路径 vector<vector<int>> paths; // 定义一个向…...

DNF手游鬼剑士攻略:全面解析流光星陨刀的获取与升级!云手机强力辅助!

《地下城与勇士》&#xff08;DNF&#xff09;手游是一款广受欢迎的多人在线角色扮演游戏&#xff0c;其中鬼剑士作为一个经典职业&#xff0c;因其强大的输出能力和炫酷的技能特效&#xff0c;吸引了众多玩家的青睐。在这篇攻略中&#xff0c;我们将详细介绍鬼剑士的一把重要武…...

npm创建一个空的vue3项目的方法或者pnpm创建vue3项目

1、前提我们已经安装了npm&#xff0c;或者pnpm 2、我们用npm来创建vue3项目 快速上手 | Vue.js 官网地址 这里我安装是的 node v18.20.3 以下是安装过程 &#xff1a; npm create vuelatest 根据自己的需要进行创建即可。 3、我们用pnpm来创建vite vue3项目 pnpm create …...

LSH算法:高效相似性搜索的原理与Python实现I

局部敏感哈希&#xff08;LSH&#xff09;技术是快速近似最近邻&#xff08;ANN&#xff09;搜索中的一个关键方法&#xff0c;广泛应用于实现高效且准确的相似性搜索。这项技术对于许多全球知名的大型科技公司来说是不可或缺的&#xff0c;包括谷歌、Netflix、亚马逊、Spotify…...

cesium 添加 Echarts图层(人口迁徒图)

cesium 添加 Echarts 人口迁徒图(下面附有源码) 1、实现思路 1、在scene上面新增一个canvas画布 2、通坐标转换,将经纬度坐标转为屏幕坐标来实现 3、将ecarts 中每个series数组中元素都加 coordinateSystem: ‘cesiumEcharts’ 2、示例代码 <!DOCTYPE html> <ht…...

Windows下快速安装Open3D-0.18.0(python版本)详细教程

目录 一、Open3D简介 1.1主要用途 1.2应用领域 二、安装Open3D 2.1 激活环境 2.2 安装open3d 2.3测试安装是否成功 三、测试代码 3.1 代码 3.2 显示效果 一、Open3D简介 Open3D 是一个强大的开源库&#xff0c;专门用于处理和可视化3D数据&#xff0c;如点云、网格和…...

无法下载 https://mirrors./ubuntu/dists/bionic/main/binary-arm64/Packages

ubuntu系统执行sudo apt update命令的时候&#xff0c;遇到如下问题&#xff1a; 忽略:82 https://mirrors.tuna.tsinghua.edu.cn/ubuntu bionic-backports/universe arm64 Packages 错误:81 https://mirrors.tuna.tsinghua.edu.cn/ubuntu bionic-backports/main arm64 Packa…...

最新CRMEB商城多商户java版源码v1.6版本+前端uniapp

CRMEB 开源商城系统Java版&#xff0c;基于JavaVueUni-app开发&#xff0c;在微信公众号、小程序、H5移动端都能使用&#xff0c;代码全开源无加密&#xff0c;独立部署&#xff0c;二开很方便&#xff0c;还支持免费商用&#xff0c;能满足企业新零售、分销推广、拼团、砍价、…...

【开发环境】MacBook M2安装git并拉取gitlab项目,解决gitlab出现Access Token使用无效的方法

文章目录 安装Homebrew安装git打开IDEA配置git打开IDEA拉取项目 安装Homebrew /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"在iTerm等命令行工具打开后&#xff0c;输入上面的命令 之后根据中文提示完成Homebrew的下载…...

Flask-Session使用Redis

Flask-Session使用Redis 一、介绍 在Flask中&#xff0c;session数据默认是以加密的cookie形式存储在用户的浏览器中的。但是&#xff0c;真正的session数据应该存储在服务器端。Django框架会将session数据存储在数据库的djangosession表中&#xff0c;而Flask则可以通过第三…...

Redis缓存管理机制

在当今快节奏的数字世界中&#xff0c;性能优化对于提供无缝的用户体验至关重要。缓存在提高应用程序性能方面发挥着至关重要的作用&#xff0c;它通过将经常使用或处理的数据存储在临时高速存储中来减少数据库负载并缩短响应时间&#xff0c;从而减少系统的延迟。Redis 是一种…...

初学嵌入式是弄linux还是单片机?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「单片机的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“666”之后私信回复“666”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;1、先入门了51先学了89c52…...

【基础算法总结】分治—快排

分治—快排 1.分治2.颜色分类3.排序数组4.数组中的第K个最大元素5.库存管理 III 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&#x1f603; 1.分治 分治…...

[C++]——同步异步日志系统(1)

同步异步日志系统 一、项⽬介绍二、开发环境三、核心技术四、环境搭建五、日志系统介绍5.1 为什么需要日志系统5.2 日志系统技术实现5.2.1 同步写日志5.2.2 异步写日志 日志系统&#xff1a; 日志&#xff1a;程序在运行过程中&#xff0c;用来记录程序运行状态信息。 作用&…...

python 第6册 辅助excel 002 批量创建非空白的 Excel 文件

---用教授的方式学习 此案例主要通过使用 while 循环以及 openpyxl. load_workbook()方法和 Workbook 的 save()方法&#xff0c;从而实现在当前目录中根据已经存在的Excel 文件批量创建多个非空白的Excel 文件。当运行此案例的Python 代码&#xff08;A002.py 文件&#xff0…...

力扣61. 旋转链表(java)

思路&#xff1a;用快慢指针找到最后链表k个需要移动的节点&#xff0c;然后中间断开节点&#xff0c;原尾节点连接原头节点&#xff0c;返回新的节点即可&#xff1b; 但因为k可能比节点数大&#xff0c;所以需要先统计节点个数&#xff0c;再取模&#xff0c;看看k到底需要移…...

智慧园区综合平台解决方案PPT(75页)

## 智慧园区的理解 ### 从园区1.0到园区4.0的演进 1. 园区1.0&#xff1a;以土地经营为主&#xff0c;成本驱动&#xff0c;提供基本服务。 2. 园区2.0&#xff1a;服务驱动&#xff0c;关注企业成长&#xff0c;提供增值服务。 3. 园区3.0&#xff1a;智慧型园区&#xff…...

Python只读取Excel文件的一部分数据,比如特定范围的行和列?

如何只读取Excel文件的一部分数据&#xff0c;比如特定范围的行和列&#xff1f; 在Python中&#xff0c;如果你只想读取Excel文件的特定范围&#xff0c;可以使用以下方法&#xff1a; pandas: Pandas是一个强大的数据处理库&#xff0c;它有一个内置函数read_excel()用于读…...

快速入门FreeRTOS心得(正点原子学习版)

对于FreeROTS&#xff0c;我第一反应想到的就是通信里的TDM&#xff08;时分多址&#xff09;。不同任务给予分配不同的时间间隔&#xff0c;也就是任务之间在每个timeslot都在来回切换。 这里有重要的一点&#xff0c;就是中断要短小&#xff0c;优先级是自高到底进行打断。 …...

【博主推荐】HTML5实现简洁好看的个人简历网页模板源码

文章目录 1.设计来源1.1 主界面1.2 关于我界面1.3 工作经验界面1.4 学习教育界面1.5 个人技能界面1.6 专业特长界面1.7 朋友评价界面1.8 获奖情况界面1.9 联系我界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c…...

Android应用安装过程

Android 系统源码源码-应用安装过程 Android 中应用安装的过程就是解析 AndroidManifest.xml 的过程&#xff0c;系统可以从 Manifest 中得到应用程序的相关信息&#xff0c;比如 Activity、Service、Broadcast Receiver 和 ContentProvider 等。这些工作都是由 PackageManage…...

Word中输入文字时,后面的文字消失

当在Word中输入文字时&#xff0c;如果发现后面的文字消失&#xff0c;通常是由以下3个原因造成的&#xff1a; 检查Insert键状态&#xff1a;首先确认是否误按了Insert键。如果是&#xff0c;请再次按下Insert键以切换回插入模式。在插入模式下&#xff0c;新输入的文字会插入…...