IO相关
  1. 常见的流

1.1 字节流

常用类如下

File读取文件

public static void main(String[] args) {
    try (
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:/Users/gaoxinyu/Desktop/wordpress.txt"));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("C:/Users/gaoxinyu/Desktop/wordpress2.txt"))
    ) {
        byte[] bytes = new byte[1024];
        int len;
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
        }
        bos.flush();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

Object读取对象

注意写完一个对象之后要等关闭之后才能读取, 因为写完之后指针指向文件末尾

代码如下:

public class Main {
    public static void main(String[] args) {
        try (
                ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:/Users/gaoxinyu/Desktop/student.txt"))
        ) {
            Student student = new Student("sanjin");
            oos.writeObject(student);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        try (
                ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:/Users/gaoxinyu/Desktop/student.txt"))
        ) {
            Student read = (Student) ois.readObject();
            System.out.println(read.getName()); // sanjin
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

class Student implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

写的是二进制数据

1.2 字符流

常用类如下

File读取文本文件

public static void main(String[] args) {
        try (
                BufferedReader in = new BufferedReader(new FileReader("C:/Users/gaoxinyu/Desktop/wordpress.txt"));
                BufferedWriter out = new BufferedWriter(new FileWriter("C:/Users/gaoxinyu/Desktop/wordpress2.txt"))
        ) {
            String line;
            while ((line = in.readLine()) != null) {
                out.write(line);
                out.newLine(); // 一行写完换行
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
}

 

2. BIO、NIO、AIO

2.1 BIO

工作机制:

  • 同步阻塞:每个连接都需要一个独立的线程来处理读写操作
  • 当线程进行读或写操作时,如果没有数据到达或数据没有写完,线程会被阻塞,直到操作完成

优点: 简单

缺点: 低效:由于线程阻塞,每个连接都会占用一个线程,线程资源消耗大

适用场景: 应用场景连接数较少且需要简单实现的程序,例如文件传输、传统的 HTTP 服务。

示例代码:

public class BIOServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);
        System.out.println("BIO Server started...");
        while (true) {
            Socket socket = serverSocket.accept(); // 阻塞等待客户端连接
            new Thread(() -> {
                try (InputStream inputStream = socket.getInputStream();
                     BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
                    String line;
                    while ((line = reader.readLine()) != null) { // 阻塞等待数据
                        System.out.println(line);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

2.2 NIO

工作机制:

  • 使用单线程管理多个连接,线程不会阻塞在某个读写操作上
  • 基于通道(Channel)和缓冲区(Buffer),数据从通道读入缓冲区,或从缓冲区写入通道
  • 使用 Selector 来监控多个通道的事件(如连接请求、可读、可写等)

优点:

  • 单线程可以管理多个连接,资源利用率高
  • 更适合高并发场景,避免了线程数与连接数直接绑定的问题

缺点: 数据处理效率依赖操作系统支持,性能未必总是优于 BIO

适用场景: 高并发、长连接的网络应用,例如即时通讯、游戏服务器

2.3 AIO

工作机制:

  • 异步非阻塞:基于事件和回调机制
  • 操作不需要线程阻塞,读写操作会交由操作系统完成,完成后通过回调通知应用程序

优点

  • 真正的异步 I/O,效率更高
  • 对编程者更友好,使用回调减少了手动处理事件的复杂性

缺点

  • 依赖操作系统的异步 I/O 支持,跨平台性较差
  • 适用场景较窄,不适合短连接、高吞吐的场景

适用场景

  • 高吞吐、低延迟的网络应用,例如高性能的文件传输、实时系统

 

3. 操作系统的IO

3.1 同步阻塞

阻塞IO就是当应用A发起读取数据申请时,在内核数据没有准备好之前,应用A会一直处于等待数据状态,直到内核把数据准备好了交给应用A才结束。

3.2 同步非阻塞

非阻塞IO就是当应用A发起读取数据申请时,如果内核数据没有准备好会即刻告诉应用A(返回错误码等),不会让A在这里等待。一旦内核中的数据准备好了,并且又再次收到了A的请求,那么它马上就将数据拷贝到了用户线程,然后返回。

3.3 IO复用

由一个线程监控多个网络请求(linux系统把所有网络请求以一个fd来标识,我们后面将称为fd即文件描述符),这样就可以只需要一个或几个线程就可以完成数据状态询问的操作,当有数据准备就绪之后再分配对应的线程去读取数据,这么做就可以节省出大量的线程资源出来,这个就是IO复用模型的思路

select(时间复杂度)O(n))

  • MARKDOWN_HASH99938282f04071859941e18f16efcf42MARKDOWNHASH线性轮询扫描所有的fd,不管他们是否活跃,监听的IO最大连接数不能多于FD SIZE(32位操作系统1024,64位操作系统2048

poll(时间复杂度O(n))

  • poll:原理和select相似,poll底层需要分配一个pollfd结构数组,维护在内核中,它没有数量限制,但IO数量大,扫描线性性能下降

epoll (时间复杂度O(1))

  • epoll :用于代替poll和select,没有大小限制。epoll采用事件驱动代替了轮询,epoll会把哪个流发生了怎样的I/O事件通知用户线程,所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时用户线程对这些流的操作都是有意义的

3.4 信号驱动

3.5 异步

在异步IO模型中,应用只需要向内核发送一个请求,告诉内核它要读取数据后即刻返回;内核收到请求后会建立一个信号联系,当数据准备就绪,内核会主动把数据从内核复制到用户空间(而信号驱动是告诉应用程序何时可以开始拷贝数据),异步IO模型真正的做到了完完全全的非阻塞

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇