网络编程基础:从BIO到NIO再到AIO(二)

BIO、NIO、AIO Java BIO 就是传统的 java io 编程,其相关的类和接口在 java.ioBIO(blocking I/O) : 同步阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池机制改善(实现多个客户连接服务器).​ 在上述的案例中,一个服务端只能接收一个客户端的通信请求,那么如果服务端需要处理很多个客户端的消息通信请求应该如何处理呢​ 在上述案例中:客户端的并发访问增加时。 阅读详情

NIO 核心概念讲解

一、NIO 是什么?

NIO = Non-blocking IO(非阻塞式输入输出)

与 BIO 的核心区别:

BIO(阻塞式):
┌──────────────┐                    ┌──────────────┐      
│   Server     │                    │   Client 1   │      
│              │◀───Socket 1────────│              │       
│  线程1专门   │                    └──────────────┘      
│  处理Client1 │                                               
│              │                    ┌──────────────┐       
│  线程2专门   │◀───Socket 2────────│   Client 2   │             
│  处理Client2 │                    └──────────────┘           
│              │
│  线程3专门   │                    ┌──────────────┐              
│  处理Client3 │◀───Socket 3────────│   Client 3   │         
└──────────────┘                    └──────────────┘               

问题:线程多,资源浪费


NIO(非阻塞式):
┌──────────────┐                    ┌──────────────┐
│   Server     │                    │   Client 1   │
│              │◀───Channel 1───────│              │
│              │                    └──────────────┘
│   Selector   │
│  (监控器)    │                    ┌──────────────┐
│              │◀───Channel 2───────│   Client 2   │
│   一个线程   │                    └──────────────┘
│   管理所有   │
│              │                    ┌──────────────┐
│              │◀───Channel 3───────│   Client 3   │
└──────────────┘                    └──────────────┘

优势:一个线程管理所有客户端

NIO 的核心特点:

  1. 非阻塞:调用方法立即返回,不会"卡住"
  2. 事件驱动:有事件才处理,没事件就等待
  3. 一个线程管理多个连接:用 Selector 监控

二、NIO 的四大核心组件

1. Selector(选择器)

作用: 监控多个 Channel 的事件

比喻: 像监控摄像头,同时监视多个通道

// 创建选择器
Selector selector = Selector.open();

// 等待事件(阻塞,但可以监控多个通道)
int count = selector.select();

// 获取就绪的事件
Set<SelectionKey> keys = selector.selectedKeys();

为什么需要 Selector?

  • BIO:一个线程只能处理一个客户端
  • NIO:一个线程通过 Selector 可以处理多个客户端

2. Channel(通道)

作用: 数据传输的管道

类型:

  • ServerSocketChannel:服务端通道,接受连接
  • SocketChannel:客户端通道,读写数据

与 BIO 的 Socket 对比:

BIONIO
ServerSocketServerSocketChannel
SocketSocketChannel
阻塞非阻塞
// 创建服务端通道
ServerSocketChannel serverChannel = ServerSocketChannel.open();

// 设置非阻塞(重要!)
serverChannel.configureBlocking(false);

// 绑定端口
serverChannel.bind(new InetSocketAddress(7397));

// 注册到选择器
serverChannel.register(selector, SelectionKey.OP_ACCEPT);

3. ByteBuffer(字节缓冲区)

作用: 读写数据的容器

为什么需要 ByteBuffer?

  • NIO 不能直接读写数据,必须通过 ByteBuffer
  • ByteBuffer 提供了灵活的读写操作

ByteBuffer 的两种模式:

写模式(初始状态):
position: 0 → 5
capacity: 10
┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
│H│e│l│l│o│ │ │ │ │ │
└─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
          ↑
       position

调用 flip() 后(切换到读模式):
position: 0
limit: 5
┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
│H│e│l│l│o│ │ │ │ │ │
└─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
↑         ↑
position  limit

核心方法:

// 创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);

// 写入数据
buffer.put(bytes);

// 切换到读模式(重要!)
buffer.flip();

// 读取数据
byte[] data = new byte[buffer.remaining()];
buffer.get(data);

// 清空缓冲区
buffer.clear();

// 快捷方式:直接包装数据
ByteBuffer buffer = ByteBuffer.wrap("hello".getBytes());

4. SelectionKey(选择键)

作用: 代表 Channel 在 Selector 上的注册信息

包含信息:

  • 哪个 Channel
  • 关注什么事件
  • 附加数据(可选)

事件类型:

事件值说明使用场景
OP_ACCEPT16接受连接服务端
OP_CONNECT8连接完成客户端
OP_READ1可读客户端和服务端
OP_WRITE4可写客户端和服务端
// 注册通道到选择器
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);

// 判断事件类型
if (key.isAcceptable()) { }   // 接受连接
if (key.isConnectable()) { }  // 连接完成
if (key.isReadable()) { }     // 可读
if (key.isWritable()) { }     // 可写

// 获取通道
SocketChannel channel = (SocketChannel) key.channel();

// 取消注册
key.cancel();

三、SimpleNioServer 详解

类的作用:

使用 NIO 实现的服务端,一个线程处理多个客户端。

核心流程图:

SimpleNioServer 启动流程:

1. 创建 Selector
   ↓
2. 创建 ServerSocketChannel
   ↓
3. 设置非阻塞模式
   ↓
4. 绑定端口 7397
   ↓
5. 注册到 Selector,监听 OP_ACCEPT
   ↓
6. 进入事件循环 while(true)
   ↓
7. selector.select() 等待事件
   ↓
8. 有事件发生,获取 selectedKeys
   ↓
9. 遍历处理每个事件:
   ├─ OP_ACCEPT:接受连接
   │  ├─ serverChannel.accept() 获取客户端通道
   │  ├─ 设置客户端通道为非阻塞
   │  ├─ 注册客户端通道到 Selector,监听 OP_READ
   │  └─ 发送欢迎消息
   │
   └─ OP_READ:读取数据
      ├─ 创建 ByteBuffer
      ├─ channel.read(buffer) 读取数据
      ├─ buffer.flip() 切换到读模式
      ├─ 解析消息
      └─ 回复客户端
   ↓
10. 回到步骤 7,继续监听

核心代码解析:

public static void main(String[] args) throws IOException {
    // ========== 步骤1-5:初始化 ==========
    
    // 1. 创建选择器(监控中心)
    Selector selector = Selector.open();
    
    // 2. 创建服务端通道
    ServerSocketChannel serverChannel = ServerSocketChannel.open();
    
    // 3. 设置为非阻塞模式(NIO 的核心!)
    //    非阻塞:调用方法立即返回,不会"卡住"
    serverChannel.configureBlocking(false);
    
    // 4. 绑定端口
    serverChannel.bind(new InetSocketAddress(7397));
    
    // 5. 把服务端通道注册到选择器
    //    监听 OP_ACCEPT 事件(接受连接)
    serverChannel.register(selector, SelectionKey.OP_ACCEPT);
    
    System.out.println("NIO 服务端启动,监听端口 7397...");
    
    // ========== 步骤6-10:事件循环 ==========
    
    while (true) {
        // 6. 等待事件发生(阻塞)
        //    虽然这里会阻塞,但可以同时监控多个通道
        //    返回值:有多少个通道就绪
        int readyChannels = selector.select();
        
        if (readyChannels == 0) {
            continue;  // 没有事件,继续等待
        }
        
        // 7. 获取所有就绪的事件
        Set<SelectionKey> selectedKeys = selector.selectedKeys();
        Iterator<SelectionKey> iterator = selectedKeys.iterator();
        
        // 8. 遍历处理每个事件
        while (iterator.hasNext()) {
            SelectionKey key = iterator.next();
            
            // 重要!处理完要移除,否则会重复处理
            iterator.remove();
            
            // 9. 根据事件类型处理
            if (key.isAcceptable()) {
                // 有客户端连接
                handleAccept(key, selector);
            } else if (key.isReadable()) {
                // 有数据可读
                handleRead(key);
            }
        }
    }
}

处理连接事件:

private static void handleAccept(SelectionKey key, Selector selector) throws IOException {
    // 1. 获取服务端通道
    ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
    
    // 2. 接受客户端连接
    //    返回客户端通道(SocketChannel)
    SocketChannel clientChannel = serverChannel.accept();
    
    System.out.println("新客户端连接:" + clientChannel.getRemoteAddress());
    
    // 3. 设置客户端通道为非阻塞
    clientChannel.configureBlocking(false);
    
    // 4. 把客户端通道注册到选择器
    //    监听 OP_READ 事件(可读)
    //    这样就能接收客户端发来的消息了
    clientChannel.register(selector, SelectionKey.OP_READ);
    
    // 5. 发送欢迎消息
    String welcome = "欢迎连接到 NIO 服务器!\n";
    ByteBuffer buffer = ByteBuffer.wrap(welcome.getBytes("UTF-8"));
    clientChannel.write(buffer);
}

处理读取事件:

private static void handleRead(SelectionKey key) throws IOException {
    // 1. 获取客户端通道
    SocketChannel clientChannel = (SocketChannel) key.channel();
    
    // 2. 创建缓冲区(1024 字节)
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    
    // 3. 从通道读取数据到缓冲区
    //    返回值:读取的字节数
    //    - 大于 0:读到数据
    //    - 等于 0:没数据
    //    - 等于 -1:连接断开
    int bytesRead = clientChannel.read(buffer);
    
    if (bytesRead == -1) {
        // 客户端断开连接
        System.out.println("客户端断开:" + clientChannel.getRemoteAddress());
        clientChannel.close();
        return;
    }
    
    if (bytesRead > 0) {
        // 4. 切换缓冲区为读模式(重要!)
        //    写完数据后,position 在末尾
        //    flip() 把 position 设为 0,limit 设为之前的 position
        //    这样就能从头读取数据了
        buffer.flip();
        
        // 5. 读取数据
        byte[] bytes = new byte[buffer.remaining()];
        buffer.get(bytes);
        String message = new String(bytes, "UTF-8").trim();
        
        System.out.println("收到消息:" + message);
        
        // 6. 回复客户端
        String response = "服务端已收到:" + message + "\n";
        ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes("UTF-8"));
        clientChannel.write(responseBuffer);
        
        // 7. 如果收到 bye,关闭连接
        if ("bye".equalsIgnoreCase(message)) {
            System.out.println("客户端主动断开:" + clientChannel.getRemoteAddress());
            clientChannel.close();
        }
    }
}

四、SimpleNioClient 详解

类的作用:

使用 NIO 实现的客户端,连接服务端并收发消息。

核心流程图:

SimpleNioClient 启动流程:

1. 创建 Selector
   ↓
2. 创建 SocketChannel
   ↓
3. 设置非阻塞模式
   ↓
4. 连接服务端(非阻塞,可能未完成)
   ↓
5. 注册到 Selector:
   ├─ 已连接:监听 OP_READ
   └─ 未连接:监听 OP_CONNECT
   ↓
6. 启动用户输入线程(发送消息)
   ↓
7. 进入事件循环 while(true)
   ↓
8. selector.select() 等待事件
   ↓
9. 遍历处理每个事件:
   ├─ OP_CONNECT:连接完成
   │  ├─ finishConnect() 完成连接
   │  └─ 改为监听 OP_READ
   │
   └─ OP_READ:读取数据
      ├─ 创建 ByteBuffer
      ├─ channel.read(buffer) 读取数据
      ├─ buffer.flip() 切换到读模式
      └─ 打印服务端消息
   ↓
10. 回到步骤 8,继续监听

核心代码解析:

public static void main(String[] args) throws IOException {
    // ========== 步骤1-5:初始化 ==========
    
    // 1. 创建选择器
    Selector selector = Selector.open();
    
    // 2. 创建客户端通道
    SocketChannel clientChannel = SocketChannel.open();
    
    // 3. 设置为非阻塞模式
    clientChannel.configureBlocking(false);
    
    // 4. 发起连接(非阻塞)
    //    非阻塞模式下,connect() 可能立即返回 false
    //    表示连接还在进行中
    boolean connected = clientChannel.connect(
        new InetSocketAddress("127.0.0.1", 7397)
    );
    
    // 5. 注册到选择器
    if (connected) {
        // 已连接,监听"可读"事件
        clientChannel.register(selector, SelectionKey.OP_READ);
        System.out.println("已连接到服务端");
    } else {
        // 未连接,监听"连接完成"事件
        clientChannel.register(selector, SelectionKey.OP_CONNECT);
        System.out.println("正在连接服务端...");
    }
    
    // ========== 步骤6:启动用户输入线程 ==========
    
    new Thread(() -> {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入消息(输入 bye 退出):");
        
        while (scanner.hasNextLine()) {
            String input = scanner.nextLine();
            try {
                // 发送消息(加换行符)
                ByteBuffer buffer = ByteBuffer.wrap(
                    (input + "\n").getBytes("UTF-8")
                );
                clientChannel.write(buffer);
                
                if ("bye".equalsIgnoreCase(input)) {
                    Thread.sleep(1000);  // 等待服务端回复
                    System.exit(0);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }).start();
    
    // ========== 步骤7-10:事件循环 ==========
    
    while (true) {
        // 等待事件
        selector.select();
        
        // 获取就绪的事件
        Set<SelectionKey> selectedKeys = selector.selectedKeys();
        Iterator<SelectionKey> iterator = selectedKeys.iterator();
        
        while (iterator.hasNext()) {
            SelectionKey key = iterator.next();
            iterator.remove();
            
            // 处理事件
            if (key.isConnectable()) {
                // 连接完成
                handleConnect(key, selector);
            } else if (key.isReadable()) {
                // 有数据可读
                handleRead(key);
            }
        }
    }
}

处理连接完成事件:

private static void handleConnect(SelectionKey key, Selector selector) throws IOException {
    SocketChannel clientChannel = (SocketChannel) key.channel();
    
    // 完成连接
    //    为什么需要这一步?
    //    因为非阻塞模式下,connect() 可能立即返回
    //    需要调用 finishConnect() 来确认连接真正完成
    if (clientChannel.finishConnect()) {
        System.out.println("连接成功!");
        
        // 改为监听"可读"事件
        clientChannel.register(selector, SelectionKey.OP_READ);
    }
}

处理读取事件:

private static void handleRead(SelectionKey key) throws IOException {
    SocketChannel clientChannel = (SocketChannel) key.channel();
    
    // 创建缓冲区
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    
    // 读取数据
    int bytesRead = clientChannel.read(buffer);
    
    if (bytesRead == -1) {
        // 服务端断开
        System.out.println("服务端断开连接");
        clientChannel.close();
        System.exit(0);
    }
    
    if (bytesRead > 0) {
        // 切换为读模式
        buffer.flip();
        
        // 读取数据
        byte[] bytes = new byte[buffer.remaining()];
        buffer.get(bytes);
        String message = new String(bytes, "UTF-8").trim();
        
        System.out.println("服务端回复:" + message);
    }
}

五、NIO vs BIO 对比

代码对比:

BIO 服务端:

// BIO:为每个客户端创建新线程
Thread clientThread = new Thread(() -> handleClient(clientSocket, clientId));
clientThread.setName("BIO-Client-" + clientId);
clientThread.start();  // 启动新线程

逻辑:来一个连接 → 创建一个线程 → 线程专门服务这个连接

NIO 服务端:

// NIO:根本不创建新线程!
// 只是把客户端通道注册到Selector
clientChannel.register(selector, SelectionKey.OP_READ, clientId);

// 然后继续循环,等待其他事件...

逻辑:来一个连接 → 注册到Selector → 继续等待事件

六、NIO代码示例

/**
 * 最简单的 NIO 服务端
 * 
 * NIO 核心:一个线程管理多个客户端
 * 关键组件:Selector(选择器)、Channel(通道)、ByteBuffer(缓冲区)
 */
public class SimpleNioServer {

    public static void main(String[] args) throws IOException {
        // 1. 创建选择器(监控中心)
        Selector selector = Selector.open();
        // 2. 创建服务端通道
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        // 3. 设置为非阻塞模式(NIO 的核心!)
        serverChannel.configureBlocking(false);
        // 4. 绑定端口
        serverChannel.bind(new InetSocketAddress(7397));
        // 5. 把服务端通道注册到选择器,监听"接受连接"事件
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("NIO 服务端启动,监听端口 7397...");
        // 6. 事件循环(核心)
        while (true) {
            // 等待事件发生(阻塞,但可以同时监控多个通道)
            // 返回值:有多少个通道就绪
            int readyChannels = selector.select();
            if (readyChannels == 0) {
                continue;  // 没有事件,继续等待
            }
            // 7. 获取所有就绪的事件
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectedKeys.iterator();
            // 8. 遍历处理每个事件
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();  // 处理完要移除,否则会重复处理
                // 9. 根据事件类型处理
                if (key.isAcceptable()) {
                    // 有客户端连接
                    handleAccept(key, selector);
                } else if (key.isReadable()) {
                    // 有数据可读
                    handleRead(key);
                }
            }
        }
    }
    
    /**
     * 处理客户端连接事件
     */
    private static void handleAccept(SelectionKey key, Selector selector) throws IOException {
        // 1. 获取服务端通道
        ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
        // 2. 接受客户端连接,返回客户端通道
        SocketChannel clientChannel = serverChannel.accept();
        System.out.println("新客户端连接:" + clientChannel.getRemoteAddress());
        // 3. 设置客户端通道为非阻塞
        clientChannel.configureBlocking(false);
        // 4. 把客户端通道注册到选择器,监听"可读"事件
        clientChannel.register(selector, SelectionKey.OP_READ);
        // 5. 发送欢迎消息
        String welcome = "欢迎连接到 NIO 服务器!\n";
        ByteBuffer buffer = ByteBuffer.wrap(welcome.getBytes("UTF-8"));
        clientChannel.write(buffer);
    }
    
    /**
     * 处理读取数据事件
     */
    private static void handleRead(SelectionKey key) throws IOException {
        // 1. 获取客户端通道
        SocketChannel clientChannel = (SocketChannel) key.channel();
        // 2. 创建缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        // 3. 从通道读取数据到缓冲区
        int bytesRead = clientChannel.read(buffer);
        if (bytesRead == -1) {
            // 客户端断开连接
            System.out.println("客户端断开:" + clientChannel.getRemoteAddress());
            clientChannel.close();
            return;
        }
        if (bytesRead > 0) {
            // 4. 切换缓冲区为读模式
            buffer.flip();
            // 5. 读取数据
            byte[] bytes = new byte[buffer.remaining()];
            buffer.get(bytes);
            String message = new String(bytes, "UTF-8").trim();
            System.out.println("收到消息:" + message);
            // 6. 回复客户端
            String response = "服务端已收到:" + message + "\n";
            ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes("UTF-8"));
            clientChannel.write(responseBuffer);
            // 7. 如果收到 bye,关闭连接
            if ("bye".equalsIgnoreCase(message)) {
                System.out.println("客户端主动断开:" + clientChannel.getRemoteAddress());
                clientChannel.close();
            }
        }
    }
}


/**
 * 最简单的 NIO 客户端
 * 
 * 功能:连接服务端,发送消息,接收回复
 */
public class SimpleNioClient {

    public static void main(String[] args) throws IOException {
        // 1. 创建选择器
        Selector selector = Selector.open();
        // 2. 创建客户端通道
        SocketChannel clientChannel = SocketChannel.open();
        // 3. 设置为非阻塞模式
        clientChannel.configureBlocking(false);
        // 4. 发起连接(非阻塞,可能立即返回 false)
        boolean connected = clientChannel.connect(new InetSocketAddress("127.0.0.1", 7397));
        // 5. 注册到选择器
        if (connected) {
            // 已连接,监听"可读"事件
            clientChannel.register(selector, SelectionKey.OP_READ);
            System.out.println("已连接到服务端");
        } else {
            // 未连接,监听"连接完成"事件
            clientChannel.register(selector, SelectionKey.OP_CONNECT);
            System.out.println("正在连接服务端...");
        }
        // 6. 启动一个线程处理用户输入
        new Thread(() -> {
            Scanner scanner = new Scanner(System.in);
            System.out.println("请输入消息(输入 bye 退出):");
            while (scanner.hasNextLine()) {
                String input = scanner.nextLine();
                try {
                    // 发送消息
                    ByteBuffer buffer = ByteBuffer.wrap((input + "\n").getBytes("UTF-8"));
                    clientChannel.write(buffer);
                    if ("bye".equalsIgnoreCase(input)) {
                        Thread.sleep(1000);  // 等待服务端回复
                        System.exit(0);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
        // 7. 事件循环
        while (true) {
            // 等待事件
            selector.select();
            // 获取就绪的事件
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectedKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();
                // 处理事件
                if (key.isConnectable()) {
                    // 连接完成
                    handleConnect(key, selector);
                } else if (key.isReadable()) {
                    // 有数据可读
                    handleRead(key);
                }
            }
        }
    }
    
    /**
     * 处理连接完成事件
     */
    private static void handleConnect(SelectionKey key, Selector selector) throws IOException {
        SocketChannel clientChannel = (SocketChannel) key.channel();
        // 完成连接
        if (clientChannel.finishConnect()) {
            System.out.println("连接成功!");
            // 改为监听"可读"事件
            clientChannel.register(selector, SelectionKey.OP_READ);
        }
    }
    
    /**
     * 处理读取数据事件
     */
    private static void handleRead(SelectionKey key) throws IOException {
        SocketChannel clientChannel = (SocketChannel) key.channel();
        // 创建缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        // 读取数据
        int bytesRead = clientChannel.read(buffer);
        if (bytesRead == -1) {
            // 服务端断开
            System.out.println("服务端断开连接");
            clientChannel.close();
            System.exit(0);
        }
        if (bytesRead > 0) {
            // 切换为读模式
            buffer.flip();
            // 读取数据
            byte[] bytes = new byte[buffer.remaining()];
            buffer.get(bytes);
            String message = new String(bytes, "UTF-8").trim();
            System.out.println("服务端回复:" + message);
        }
    }
}

BIO,NIO,多路复用,AIO BIO,NIO,多路复用,AIO 概念理解 IO流程 三大IO模型的解释 总结 BIO,NIO,多路复用,AIO 概念理解 同步和异步 关注点在于处理机制,同步在于等待上一条语句调用后的返回结果才继续,异步在于事件响应回调机制,语句调用后直接返回,等触发事件之后,自动调用写好的回调函数 阻塞非阻塞 关注点在于线程是否挂起 阻塞在于得到结果之前会挂起线程,非阻塞在于不会... 阅读详情

相关推荐

Netty(一)——BIO、NIO、AIO

         在工作中,无论什么项目我们都会用到网络传输,tomcat等server服务器、Dubbo等Rpc协议框架、Redis等NOsql数据库、各种MQ的中间件等等。这篇文章是学习《Netty权威指南》后的总结,从Java基础的BIO、NIO、AIO进行学习。         在说JavaIO以前,先看看unix提供的5种IO模型:         1,阻塞IO模型:    ...

学会改变自己——才能突破 1311

BIO、NIO、AIO详解

一、Java的I/O演进之路 Java共支持3种网络编程的I/O模型:BIO、NIO、AIO BIO: 同步并阻塞(传统阻塞型),服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销 NIO: 同步非阻塞,服务器实现模式为一个线程处理多个请求(连接),即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求就进行处理 AIO: 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求

hxt的博客 2万+

接口调试必看:status显示canceled的3种常见原因及修复方法

本文深度解析了接口调试中status显示canceled的三种核心原因:网络层中断(如请求超时)、应用层逻辑主动取消(如请求竞态、组件卸载)以及用户交互与浏览器行为。文章提供了从诊断到修复的系统性方案,包括分析Network Timing、使用Axios CancelToken、优化请求策略与性能,帮助开发者从根本上解决这一常见问题。

bb456的博客 937

面试官:BIO、NIO、AIO是什么,他们有什么区别?

面试官:BIO、NIO、AIO是什么,他们有什么区别?

真正的大师永远怀着一颗学徒的心 9948

第五章 TTM 后备管理:5.6系统内存域

系统内存域由 TTM 内置资源管理器管理,通过 ttm_tt 承载页信息,依赖 ttm_pool 提供物理页。ttm_tt 管理页数组、缓存属性与状态标志,支持外部页与换出机制。初始化分 ttm_tt_init 与 ttm_sg_tt_init,生命周期包括创建、填充、释放与销毁。页池针对 write-combined、uncached 及一致性 DMA 分配的页进行缓存,提升性能并减少 TLB 刷新。分配时高阶优先、批量设置缓存属性,回收时清零后归还池中,确保高效复用。

图形/计算GPU的驱动与虚拟化技术分享园地 2572

面向元数据的 SAP UI 中枢:namespace sap.ui.mdc 全景解析

如果把应用前端比作一条数据到交互的装配线,sap.ui.mdc 就是那条线的标准化工装:以 PropertyInfo 描述原料,以 Delegate 定义装配方法,以 p13n 与 VariantManagement 交付面向个体的最终产物;而 Condition Model、Operator 与 ValueHelp 则让输入与筛选的语义落地。与 OData V4 服务与 Fiori Elements 宏构件组合使用时,它能在保持一致体验的同时,给自由式开发者留下足够的可编排空间。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 238

BIO、NIO、AIO 有什么区别?

BIO、NIO、AIO 有什么区别? BIO:线程发起IO请求,不管内核是否准备好IO操作,从发起请求起,线程一直阻塞,直到操作完成。 NIO:线程发起IO请求,立即返回;内核在做好IO操作的准备之后,通过调用注册的回调函数通知线程做IO操作,线程开始阻塞,直到操作完成。 AIO:线程发起IO请求,立即返回;内存做好IO操作的准备之后,做IO操作,直到操作完成或者失败,通过调用注册的回调...

ConstXiong 2万+

Qt EGLFS 图形后端详解

EGLFS (EGL Full Screen) 是Qt框架在嵌入式Linux系统中的高性能图形显示后端,它提供了直接基于EGL和OpenGL ES的硬件加速渲染解决方案。

浩瀚之水的专栏 513

BIO,NIO,AIO的区别

BIO,NIO和AIO是Java网络编程的三种模型 BIO:同步并阻塞,服务实现模式为一个连接对应一个线程,即客户端发送一个连接,服务端要有一个线程来处理。如果连接多了,线程数量不够,就只能等待,即会发生阻塞。 NIO:同步非阻塞,服务实现模式是一个线程可以处理多个连接,即客户端发送的连接都会注册到多路复用器上,然后进行轮询连接,有I/O请求就处理 AIO:异步非阻塞,引入了异步通道,采用的是proactor模式,特点是:有效的请求才启动线程,先有操作系统完成在通知服务端 应用场景: BIO:

qxy_1218的博客 1万+

5、模糊风险评估

本文介绍了基于模糊逻辑的风险评估方法,探讨了如何在不确定、不完整信息环境下进行高效的风险分析。文章涵盖了模糊集合与隶属度函数的基本概念、模糊推理系统的流程,并结合犯罪风险、金融风险和医疗诊断等实际案例,展示了模糊风险评估的应用价值和技术细节。此外,还介绍了模糊贝叶斯网络、模糊综合评价等进阶工具与方法,为多领域风险管理提供了理论支持和实践指导。

mango的博客 183

无人飞行器和车辆的协同配送物资问题——附带MATLAB代码

通过建立城市地图、生成物资点和配送任务、路径规划、协同调度以及仿真和评估等步骤,可以有效地解决物资配送的优化问题。通过建立城市地图、生成物资点和配送任务、路径规划、协同调度以及仿真和评估等步骤,可以有效地解决物资配送的优化问题。我们的目标是最小化物资配送的总时间,同时确保每个物资点都被正确配送的无人飞行器或车辆所访问。我们的目标是最小化物资配送的总时间,同时确保每个物资点都被正确配送的无人飞行器或车辆所访问。我们首先需要建立一个城市地图,包括物资点、无人飞行器的起始点和车辆的起始点。

CyberJolt的博客 951

AIO,BIO,NIO详解

一、基本概念 IO:阻塞IO BIO:同步阻塞IO。服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器需要启动一个线程进行处理,如果这个链接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。 NIO:同步非阻塞IO,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求时才启动一个线程进行处理。用户进程也需要时不时的询...

不务正业随手记 6737

【国产MCU】-认识CH32V307及开发环境搭建

CH32V307是[沁恒](https://www.wch.cn/)推出的一款基于32位RISC-V设计的互联型微控制器,配备了硬件堆栈区、快速中断入口,在标准RISC-V基础上大大提高了中断响应速度。加入单精度浮点指令集,扩充堆栈区,具有更高的运算性能。扩展串口UART数量到8组,电机定时器到4组。提供USB2.0高速接口(480Mbps)并内置了PHY收发器,以太网MAC升级到千兆并集成了10M-PHY模块。

视觉与物联智能 2346

一文搞懂Java BIO、NIO、AIO的核心区别

简单应用、低并发场景,选 BIO 足够,编程成本低;高并发网络编程场景,选 NIO,多路复用能大幅提升性能;高并发且 IO 操作耗时较长的场景,选 AIO,异步通知能最大化利用线程资源。掌握这三种 IO 模型的核心区别,能帮助我们在实际开发中做出更合理的技术选型,写出更高效的 Java 程序。

2301_79892101的博客 2358

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

BIO、NIO与AIO

I/O 模型:就是用什么样的通道或者说是通信模式和架构进行数据的传输和接收,很大程度上决定了程序通信的性能,Java 共支持 3 种网络编程的/IO 模型:**BIO、NIO、AIO** 1 每当接收到一个Socket连接就会创建一个新的线程,线程的竞争以及上下文切换会影响性能;2 每个线程都会占用栈空间和CPU资源;3 并不是每个socket都进行IO操作,无意义的线程处理(即使客户端没有消息,服务端的线程也会阻塞等待);

weixin_42828342的博客 1718

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

JAVAIO模型之NIO、BIO、AIO

IO模型 io模型就是各种数据使用相应通道进行发送和接收。Java共支持三种网络编程IO模式 BIO、NIO、AIO BIO (Blocking IO) 同步阻塞模型,一个客户端连接对应一个处理线程。 缺点 IO代码里read是阻塞操作,如果连接不做读写操作会导致线程阻塞,浪费资源 如果读写很多,会导致服务器线程过多,压力太大。 应用场景 BIO适用于连接数目较小且固定的架构,这种方式对服务器资源的要求比较高,但是程序简单易理解。 示例代码 /** * 服务端 * @author 风信子 */

Zev_java的博客 1421

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Java IO 模型之 BIO、NIO、AIO 详解

在 Java 编程中,IO(Input/Output)操作是非常常见的操作,它涉及到文件读写、网络通信等方面。Java 提供了各种类来支持这些操作。本文将从 IO 的基础知识讲起,逐步深入,介绍 Java IO 的各个方面。Java IO(Input/Output)是 Java 语言中用于读写数据的 API,它提供了一系列类和接口,用于读取和写入各种类型的数据。

流华追梦的专栏 2117

详解BIO,NIO,AIO

详解BIO,NIO,AIO

m0_66220639的博客 1258

面试官:BIO、NIO、AIO 的区别是什么?

这是个网络编程的面试题:在讲 BIO,NIO,AIO 之前先来回顾一下这样几个概念:同步与异步,阻塞与非阻塞。自己的理解:也就是必须一件一件事做,等前一件做完了才能做下一件事 (提交请求->等待服务器处理->处理完返回 这个期间客户端浏览器不能干任何事 )官方:使用同步IO时,Java自己处理IO读写。

2401_85910670的博客 1147

BIO、NIO、AIO的区别?

Java 中的 BIO、NIO和 AIO 理解为是 Java 语言对操作系统的各种 IO 模型的封装。程序员在使用这些 API 的时候,不需要关心操作系统层面的知识,也不需要根据不同操作系统编写不同的代码。只需要使用Java的API就可以了。

西京刀客 1256

3 JDK 常见的包和BIO,NIO,AIO

NIO (New I/O):NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了NIO框架,对应 java.nio包,提供了 Channel , Selector,Buffer等抽象。非阻塞模式正好与之相反。AIO 是异步IO的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。

yusongcao的博客 2153

Java的BIO、NIO、AIO模型

NIO方式适用于连接数目多且连接比较短(轻操作) 的架构, 比如聊天服务器, 弹幕系统, 服务器间通讯,编程比较复杂, JDK1.4 开始支持。2、channel 会注册到 selector 上,由 selector 根据 channel 读写事件的发生将其交由某个空闲的线程处理。同步非阻塞,服务器实现模式为一个线程可以处理多个请求(连接),客户端发送的连接请求都会注册到。1、IO代码里read操作是阻塞操作,如果连接不做数据读写操作会导致线程阻塞,浪费资源。同步阻塞模型,一个客户端连接对应一个处理线程。

weixin_42037190的博客 1048

BIO,NIO,AIO 简要总结

文章目录BIO,NIO,AIO 总结1. BIO (Blocking I/O)1.1 传统 BIO1.2 伪异步 IO1.3 代码示例1.4 总结2. NIO (New I/O)2.1 NIO 简介2.2 NIO的特性/NIO与IO区别1)Non-blocking IO(非阻塞IO)2)Buffer(缓冲区)3)Channel (通道)4)Selectors(选择器)2.3 NIO 读数据和写数据...

VicterTian的博客 1732
上一篇: 网络编程基础:从BIO到NIO再到AIO(一)
下一篇: 网络编程基础:从BIO到NIO再到AIO(三)
不早睡不改名@
博客等级 码龄5年 536粉丝 306原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值