目录

1.同步与异步

2.阻塞和非阻塞

3.BIO,NIO,AIO

3.1 BIO (Blocking I/O)

3.2 NIO (No-Blocking I/O)

3.3 AIO (Asynchronous I/O)


在讲 BIO,NIO,AIO 之前先来回顾一下这样几个概念:同步与异步,阻塞与非阻塞。

1.同步与异步

  • 同步调用(Synchronous Cal): 同步就是发起一个调用后,被调用者未处理完请求之前,调用不返回        

        代码示例:

int result = calculateSum(3, 5); // 等到函数调用完成后,获取返回结果
System.out.println(result);      // 输出计算结果 8

public static int calculateSum(int a, int b) {
    // 同步调用:当前线程会阻塞,直到函数计算完成并返回结果
    return a + b;
}
  • 异步调用(Asynchronous Call): 异步就是发起一个调用后,立刻得到被调用者的回应表示已接收到请求,但是被调用者并没有返回结果,此时我们可以处理其他的请求,被调用者通常依靠事件,回调等机制来通知调用者其返回结果。

        代码示例(使用回调):  调用者传递一个函数给被调用者,被调用者在完成后调用该函数。

interface Callback {
    void onComplete(int result);
}

void calculateSumAsync(int a, int b, Callback callback) {
    new Thread(() -> {
        int result = a + b;
        callback.onComplete(result); // 完成后通知调用者
    }).start();
}

// 调用
calculateSumAsync(3, 5, (result) -> System.out.println("结果: " + result));

         代码示例(使用事件):被调用者通过触发事件(如按钮点击、网络请求完成)通知调用者。

class EventEmitter {
    private List<EventListener> listeners = new ArrayList<>();

    void addListener(EventListener listener) {
        listeners.add(listener);
    }

    void doWork() {
        new Thread(() -> {
            int result = 42;
            for (EventListener listener : listeners) {
                listener.onEvent(result); // 触发事件
            }
        }).start();
    }
}

interface EventListener {
    void onEvent(int data);
}

// 调用
EventEmitter emitter = new EventEmitter();
emitter.addListener(data -> System.out.println("收到事件: " + data));
emitter.doWork();

        同步调用中调用者发出请求后,必须阻塞等待被调用者完成处理并返回结果,异步调用中调用者发出请求后立即获得"已接收"响应,但此时可能尚未得到最终处理结果

2.阻塞和非阻塞

  • 阻塞: 阻塞就是发起一个请求,调用者一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当条件就绪才能继续。

  • 非阻塞: 非阻塞就是发起一个请求,调用者不用一直等着结果返回,可以先去干其他事情。

        阻塞是指线程在等待某个条件或资源时被挂起,不能继续执行;而非阻塞是指线程不会被挂起,操作立即返回,能快速判断是否成功继续执行。阻塞常用于简单同步场景,但性能较低;非阻塞适合高并发环境,效率更高。


同/异步与阻塞/非阻塞区别:        

        同步与异步主要关注的是调用结果的获取方式:同步调用中,调用方在发起请求后需要等待操作完成并直接获得返回结果;而异步调用中,调用方发起请求后无需立刻获取结果,结果会在任务完成后通过回调、Future 或事件通知等方式返回。
        阻塞与非阻塞主要关注的是等待期间线程的执行状态:阻塞表示线程在等待结果时被挂起,无法执行其他任务;非阻塞表示线程在等待期间不会被挂起,仍可以继续执行其他逻辑。


3.BIO,NIO,AIO

3.1 BIO (Blocking I/O)

        BIO 是 Java 早期最经典的 I/O 模型,核心特征是 同步 + 阻塞:线程发起读/写后,会一直阻塞等待数据就绪以及拷贝完成,期间不能做其他事情。在服务端模型中,常见写法是 一个连接对应一个线程(或线程池),实现简单但并发高时线程开销大、容易出现上下文切换和资源耗尽问题。

// BIO 服务端:阻塞式接收 + 阻塞式读取(一个连接一个线程示例)
import java.io.*;
import java.net.*;

public class BioServer {
    public static void main(String[] args) throws Exception {
        ServerSocket server = new ServerSocket(8080);
        System.out.println("BIO server start...");

        while (true) {
            Socket socket = server.accept(); // 阻塞:等待连接
            new Thread(() -> {
                try (BufferedReader in = new BufferedReader(
                        new InputStreamReader(socket.getInputStream()));
                     PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {

                    String line = in.readLine(); // 阻塞:等待数据
                    out.println("BIO reply: " + line);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

        适用场景:低并发、连接数不多、开发快速验证;或业务处理很重、连接少(线程数可控)。

3.2 NIO (No-Blocking I/O)

         NIO 是 Java 1.4 引入的 I/O 框架,对应 java.nio 包。NIO 的核心是 同步 + 非阻塞:读写调用可以立即返回(可能读到 0 字节),应用通过 Selector 多路复用在一个线程内管理多个连接,减少线程数量与上下文切换成本。NIO 采用 Buffer(缓冲区)+ Channel(通道)的方式进行数据读写,并支持阻塞/非阻塞两种模式,但在高并发网络编程中通常使用 非阻塞 + Selector

// NIO 服务端:Selector 多路复用(单线程处理多个连接)
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

public class NioServer {
    public static void main(String[] args) throws Exception {
        ServerSocketChannel server = ServerSocketChannel.open();
        server.bind(new InetSocketAddress(8081));
        server.configureBlocking(false);

        Selector selector = Selector.open();
        server.register(selector, SelectionKey.OP_ACCEPT);

        System.out.println("NIO server start...");

        ByteBuffer buf = ByteBuffer.allocate(1024);

        while (true) {
            selector.select(); // 阻塞等待“就绪事件”(可认为等待通知)
            Iterator<SelectionKey> it = selector.selectedKeys().iterator();

            while (it.hasNext()) {
                SelectionKey key = it.next();
                it.remove();

                if (key.isAcceptable()) {
                    SocketChannel sc = server.accept();
                    sc.configureBlocking(false);
                    sc.register(selector, SelectionKey.OP_READ);
                } else if (key.isReadable()) {
                    SocketChannel sc = (SocketChannel) key.channel();
                    buf.clear();
                    int n = sc.read(buf); // 非阻塞读:可能返回 0 或 -1
                    if (n <= 0) {
                        sc.close();
                        continue;
                    }
                    buf.flip();
                    sc.write(ByteBuffer.wrap(("NIO reply: ").getBytes()));
                    sc.write(buf);
                }
            }
        }
    }
}

        适用场景:高并发连接、多连接 I/O 频繁(网络服务器、网关、聊天室等),希望用少量线程处理大量连接。

3.3 AIO (Asynchronous I/O)

        AIO(也常称 NIO.2)在 Java 7 引入,对应 java.nio.channels.Asynchronous 系列。AIO 的核心特征是 异步 + 非阻塞:应用发起 I/O 操作后立即返回,不需要自己轮询或等待,操作完成后由系统通过回调(CompletionHandler)或 Future 通知结果。相比 NIO 的“同步非阻塞(事件就绪后自己读写)”,AIO 更接近“真正的异步”:你只需要提交任务,完成后得到通知。

// AIO 服务端:异步 accept + 异步 read(回调驱动)
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.concurrent.CountDownLatch;

public class AioServer {
    public static void main(String[] args) throws Exception {
        AsynchronousServerSocketChannel server =
                AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8082));

        System.out.println("AIO server start...");

        CountDownLatch latch = new CountDownLatch(1);

        server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
            @Override
            public void completed(AsynchronousSocketChannel ch, Void att) {
                // 继续接收下一个连接(非常关键,否则只接一次)
                server.accept(null, this);

                ByteBuffer buf = ByteBuffer.allocate(1024);
                ch.read(buf, buf, new CompletionHandler<Integer, ByteBuffer>() {
                    @Override
                    public void completed(Integer n, ByteBuffer b) {
                        try {
                            if (n <= 0) { ch.close(); return; }
                            b.flip();
                            ch.write(ByteBuffer.wrap("AIO reply: ".getBytes())).get();
                            ch.write(b).get();
                            ch.close();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    @Override
                    public void failed(Throwable exc, ByteBuffer b) {
                        try { ch.close(); } catch (Exception ignored) {}
                    }
                });
            }

            @Override
            public void failed(Throwable exc, Void att) {
                exc.printStackTrace();
                latch.countDown();
            }
        });

        latch.await(); // 防止主线程退出
    }
}

BIO、AIO、NIO小结:

        BIO:同步阻塞,简单但并发高会吃线程

        NIO:同步非阻塞,Selector 管理多连接,适合高并发

        AIO:异步非阻塞,回调/通知获取结果,编程模型更异步


Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐