Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
467 changes: 467 additions & 0 deletions src/main/java/org/example/java_base_test/TreeSetCompare.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,44 @@ static void explain() {
System.out.println(" 真相:连接数少(<100)时,BIO 代码更简单,性能差不多");
System.out.println(" 连接数多(>1000)时,NIO 才有明显优势");
System.out.println(" 本质:BIO 瓶颈是线程数,NIO 瓶颈是 CPU 处理能力");
System.out.println(" 小项目用 NIO 是过度设计,代码复杂但收益不大");
System.out.println();
System.out.println(" 误区2:以为 selector.select() 是忙等(CPU spin)");
System.out.println(" 真相:是真正的线程休眠(epoll_wait),该线程不占用 CPU 时间片");
System.out.println(" 内核有事件时才唤醒线程,属于事件驱动,不是轮询");
System.out.println();
System.out.println(" 误区3:OP_WRITE 一直注册");
System.out.println(" 真相:发送缓冲区几乎一直有空间,OP_WRITE 几乎一直就绪");
System.out.println(" select() 一直立刻返回 → CPU 100%!");
System.out.println(" 正确:只在写不完时注册,写完立刻取消");
System.out.println(" 类比:一直问“能发货吗”,快递员被你烦死了");
System.out.println();
System.out.println(" 误区4:selectedKeys() 不 remove()");
System.out.println(" 真相:已处理的 key 不自动移除,下次 select() 还会返回");
System.out.println(" → 同一事件重复处理");
System.out.println(" → 同一事件重复处理,数据错乱或重复发送");
System.out.println(" 必须手动 iterator.remove(),否则逻辑混乱");
System.out.println();
System.out.println(" 误区5:FileChannel 以为能注册 Selector");
System.out.println(" 真相:FileChannel 不支持非阻塞模式,不能注册 Selector");
System.out.println(" 磁盘 IO 是同步的,没有“就绪”概念,只能用阻塞方式读写");
System.out.println();
System.out.println(" 误区6:transferTo 不写 while 循环");
System.out.println(" 真相:网络场景一次可能传不完,不写 while 会静默丢数据");
System.out.println(" 本地文件传输一般一次搞定,但网络传输必须循环检查");
System.out.println(" 直到 transferred == count 才算完成");
System.out.println();
System.out.println(" 误区7:Netty handler 里做阻塞操作(数据库查询)");
System.out.println(" 真相:会把 EventLoop 线程卡死,整个服务停响");
System.out.println(" 解决:ctx.executor().execute(() → { 阻塞操作 → 回写 })");
System.out.println(" 或用 eventLoop.submit() 提交到业务线程池");
System.out.println();
System.out.println(" ★ EventLoop 本质:一个线程 + 一个 Selector + 管理 N 个 Channel");
System.out.println(" Netty 采用 Reactor 主从模式,Boss 线程负责 accept,Worker 线程负责读写");
System.out.println(" 每个 Channel 绑定一个 EventLoop,同一 Channel 的事件始终在同一线程处理");
System.out.println(" → 无锁化设计,无需 synchronized,这是高并发的关键");
System.out.println(" → 一旦 handler 阻塞,该 EventLoop 负责的所有 Channel 都无法响应");
System.out.println(" → 相当于一个快递员被绑在厕所,整片小区没人送快递");
System.out.println();

System.out.println("二、CPU 拷贝 vs DMA 拷贝(容易混淆)");
System.out.println();
Expand All @@ -81,19 +95,19 @@ static void explain() {

System.out.println("三、技术选型指南");
System.out.println();
System.out.println(" ┌─────────────────────────┬────────────────────────────────────┐");
System.out.println(" │ 场景 │ 推荐方案 │");
System.out.println(" ├─────────────────────────┼────────────────────────────────────┤");
System.out.println(" │ 读写本地小文件 │ BufferedInputStream/OutputStream │");
System.out.println(" │ │ 或 Files.readAllBytes(< 64MB) │");
System.out.println(" │ 大文件随机读取(GB级) │ MappedByteBuffer(内存映射) │");
System.out.println(" │ 本地文件传输到网络 │ FileChannel.transferTo(零拷贝) │");
System.out.println(" │ 自己写 HTTP/TCP 服务 │ Netty(不要用原生NIO,太复杂) │");
System.out.println(" │ 微服务 RPC │ Dubbo/gRPC(底层都是 Netty) │");
System.out.println(" │ 消息队列 │ Kafka(零拷贝+顺序写) │");
System.out.println(" │ 高并发 Web 静态资源 │ Nginx(sendfile) │");
System.out.println(" │ 高并发 Web 动态接口 │ Spring Boot(底层 Netty/Undertow) │");
System.out.println(" └─────────────────────────┴────────────────────────────────────┘");
System.out.println(" ┌─────────────────────────┬────────────────────────────────────┬─────────────────────────────────────┐");
System.out.println(" │ 场景 │ 推荐方案 │ 原因 │");
System.out.println(" ├─────────────────────────┼────────────────────────────────────┼─────────────────────────────────────┤");
System.out.println(" │ 读写本地小文件 │ BufferedInputStream/OutputStream │ Buffer 缓冲够用,代码简单性能好 │");
System.out.println(" │ │ 或 Files.readAllBytes(< 64MB) │");
System.out.println(" │ 大文件随机读取(GB级) │ MappedByteBuffer(内存映射) │ mmap 映射到内存,随机访问像数组一样快 │");
System.out.println(" │ 本地文件传输到网络 │ FileChannel.transferTo(零拷贝) │ 数据直接从 Page Cache 通过 DMA 发出 │");
System.out.println(" │ 自己写 HTTP/TCP 服务 │ Netty(不要用原生NIO,太复杂) │ 原生 NIO 七大坑,Netty 全部封装好了 │");
System.out.println(" │ 微服务 RPC │ Dubbo/gRPC(底层都是 Netty) │ 序列化、服务发现、负载均衡都内置了 │");
System.out.println(" │ 消息队列 │ Kafka(零拷贝+顺序写) │ 顺序写 + sendfile,吞吐量 GB/s 级别 │");
System.out.println(" │ 高并发 Web 静态资源 │ Nginx(sendfile) │ 静态文件直接从磁盘 DMA 到网卡 │");
System.out.println(" │ 高并发 Web 动态接口 │ Spring Boot(底层 Netty/Undertow) │ Netty 处理连接 + Spring 开发效率高 │");
System.out.println(" └─────────────────────────┴────────────────────────────────────┴─────────────────────────────────────┘");
System.out.println();

System.out.println("四、整体知识体系一张图");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
package org.example.java_base_test.io.nio.show_multi_agent;

import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.nio.file.Files;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.nio.channels.SeekableByteChannel;
import java.util.Set;
import java.nio.file.attribute.PosixFilePermission;

class Part14_FileChannelLocalIO {
public class Part14_FileChannelLocalIO {

public static void main(String[] args) throws Exception {
demonstrate();
}

static void demonstrate() throws Exception {
System.out.println("【第十四部分:FileChannel 本地文件 IO 完整操作】");
Expand Down Expand Up @@ -55,6 +71,12 @@ static void demonstrate() throws Exception {
System.out.println(" FileChannel writeCh = new FileOutputStream(file).getChannel();");
System.out.println(" FileChannel rwCh = new RandomAccessFile(file, \"rw\").getChannel();");
System.out.println();
System.out.println(" // 方式三:使用 Files.newByteChannel() 返回 SeekableByteChannel");
System.out.println(" SeekableByteChannel sbc = Files.newByteChannel(path, options);");
System.out.println(" if (sbc instanceof FileChannel) {");
System.out.println(" FileChannel fc = (FileChannel) sbc;");
System.out.println(" }");
System.out.println();
System.out.println(" ⚠ 注意:关闭 Channel 会自动关闭关联的 Stream,反之亦然");
System.out.println(" Stream 和 Channel 不要各自关,关一个就够了");
System.out.println();
Expand Down Expand Up @@ -110,7 +132,7 @@ static void demonstrate() throws Exception {

java.nio.file.Path tmpFile2 = java.nio.file.Files.createTempFile("fc_pos_", ".txt");
try {
// 先写 "ABCDEFGHIJ"(10字节)
// 先写 "ABCDE FGHIJ"(10字节)
try (java.nio.channels.FileChannel fc =
java.nio.channels.FileChannel.open(tmpFile2,
java.nio.file.StandardOpenOption.WRITE,
Expand Down Expand Up @@ -203,7 +225,7 @@ static void demonstrate() throws Exception {
System.out.println(" 场景:两个 JVM 进程同时写同一个文件 → 数据损坏");
System.out.println(" FileLock 是操作系统级别的锁,不同进程都能感知");
System.out.println(" 注意:同一个 JVM 内多线程用 FileLock 无效,要用 synchronized");
System.out.println();
System.out.println( );
System.out.println(" 两种锁:");
System.out.println(" 独占锁(写锁):fc.lock() → 其他进程的 lock() 会阻塞");
System.out.println(" 共享锁(读锁):fc.lock(0, Long.MAX_VALUE, true)");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ static void demonstrate() throws Exception {

java.nio.file.Path p = java.nio.file.Paths.get("/Users/demo/data/test.txt");
System.out.println(" Path p = Paths.get(\"/Users/demo/data/test.txt\")");
System.out.println(" p.getFileName() = " + p.getFileName());
System.out.println(" p.getParent() = " + p.getParent());
System.out.println(" p.getRoot() = " + p.getRoot());
System.out.println(" p.getNameCount() = " + p.getNameCount() + " (路径分量数)");
System.out.println(" p.getName(1) = " + p.getName(1) + " (第2段路径)");
System.out.println(" p.isAbsolute() = " + p.isAbsolute());
System.out.println(" p.getFileName() = " + p.getFileName()); // 获取文件名:test.txt(最后一段)
System.out.println(" p.getParent() = " + p.getParent()); // 获取父目录:/Users/demo/data
System.out.println(" p.getRoot() = " + p.getRoot()); // 获取根目录:/(Unix系统)
System.out.println(" p.getNameCount() = " + p.getNameCount() + " (路径分量数)"); // 3个分量:Users、data、test.txt
System.out.println(" p.getName(1) = " + p.getName(1) + " (第2段路径)"); // 索引从0开始:0=Users, 1=data, 2=test.txt
System.out.println(" p.isAbsolute() = " + p.isAbsolute()); // true:以 / 开头的是绝对路径
System.out.println();

// resolve:拼接路径
Expand Down
Loading