首页 > 其他分享 >IO与NIO的区别

IO与NIO的区别

时间:2024-05-27 19:12:02浏览次数:20  
标签:netty NIO io 区别 IO new import channel

IO与NIO的区别及其应用案例

Java中的IO(Input/Output)和NIO(New Input/Output)是用于处理数据传输的两种不同的API。它们在设计理念、实现方式和适用场景上有显著的差异。本文将详细介绍IO与NIO的区别,并通过实际案例说明如何应用NIO。

IO与NIO的区别

1. 阻塞与非阻塞

  • IO(阻塞IO,BIO):传统IO以阻塞模式工作,当一个线程进行读写操作时,该线程会被阻塞,直到操作完成。每个连接需要一个线程来处理,多个连接需要多个线程。

    try (InputStream inputStream = socket.getInputStream()) {
        byte[] buffer = new byte[1024];
        int bytesRead = inputStream.read(buffer); // 阻塞,直到有数据可读
    } catch (IOException e) {
        e.printStackTrace();
    }
    
  • NIO(非阻塞IO):NIO以非阻塞模式工作,允许一个线程处理多个连接,可以在数据未准备好时返回,而不是阻塞等待。通过选择器(Selector)机制,一个线程可以管理多个通道(Channel)。

    try (Selector selector = Selector.open();
         ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {
        serverSocketChannel.bind(new InetSocketAddress(8080));
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    
        while (true) {
            selector.select(); // 阻塞,直到有事件发生
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectedKeys.iterator();
    
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                if (key.isAcceptable()) {
                    ServerSocketChannel server = (ServerSocketChannel) key.channel();
                    SocketChannel client = server.accept();
                    client.configureBlocking(false);
                    client.register(selector, SelectionKey.OP_READ);
                } else if (key.isReadable()) {
                    SocketChannel client = (SocketChannel) key.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    client.read(buffer); // 非阻塞
                }
                iterator.remove();
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    

2. 基本单元

  • IO:以流(Stream)为基本操作单元,数据一个字节一个字节地传输。

    InputStream inputStream = new FileInputStream("data.txt");
    int data = inputStream.read();
    while (data != -1) {
        System.out.print((char) data);
        data = inputStream.read();
    }
    inputStream.close();
    
  • NIO:以块(Block)为基本操作单元,数据以块的形式进行传输,效率更高。

    RandomAccessFile file = new RandomAccessFile("data.txt", "rw");
    FileChannel channel = file.getChannel();
    ByteBuffer buffer = ByteBuffer.allocate(48);
    int bytesRead = channel.read(buffer);
    while (bytesRead != -1) {
        buffer.flip();
        while (buffer.hasRemaining()) {
            System.out.print((char) buffer.get());
        }
        buffer.clear();
        bytesRead = channel.read(buffer);
    }
    file.close();
    

3. 选择器机制

  • IO:没有选择器机制,每个连接都需要一个线程来处理。

  • NIO:引入了选择器(Selector)机制,一个线程可以管理多个通道(Channel),大大提高了资源利用率和处理效率。

    Selector selector = Selector.open();
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    serverSocketChannel.configureBlocking(false);
    serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    

4. 可扩展性

  • IO:每个连接需要一个线程处理,高并发场景下,创建和管理大量线程的开销非常大,扩展性较差。

  • NIO:通过非阻塞I/O和选择器机制,一个线程可以处理多个连接,减少了线程开销,提高了系统的扩展性和性能。

5. 使用场景

  • IO:适用于连接数量较少、处理逻辑复杂或需要简单实现的场景,例如小型应用、脚本工具等。

  • NIO:适用于高并发、大数据量传输或实时性要求高的场景,例如高性能服务器、聊天室、实时数据处理系统等。

NIO的经典应用案例:Netty Echo 服务器

Netty是基于NIO的高性能网络应用框架,简化了NIO编程,提供了高级特性。下面是使用Netty实现的Echo服务器和客户端的示例。

服务器端代码:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

public class NettyEchoServer {

    private final int port;

    public NettyEchoServer(int port) {
        this.port = port;
    }

    public void start() throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 100)
                .handler(new LoggingHandler(LogLevel.INFO))
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new StringDecoder());
                        ch.pipeline().addLast(new StringEncoder());
                        ch.pipeline().addLast(new NettyEchoServerHandler());
                    }
                });

            ChannelFuture f = b.bind(port).sync();
            f.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        int port = 8080;
        new NettyEchoServer(port).start();
    }
}

服务器处理器代码:

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class NettyEchoServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        System.out.println("Server received: " + msg);
        ctx.write(msg); // 将消息写回客户端
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush(); // 刷新消息
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close(); // 关闭连接
    }
}

客户端代码:

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class NettyEchoClient {

    private final String host;
    private final int port;

    public NettyEchoClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public void start() throws InterruptedException {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new StringDecoder());
                        ch.pipeline().addLast(new StringEncoder());
                        ch.pipeline().addLast(new NettyEchoClientHandler());
                    }
                });

            ChannelFuture f = b.connect(host, port).sync();
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new NettyEchoClient("localhost", 8080).start();
    }
}

客户端处理器代码:

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class NettyEchoClientHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        ctx.writeAndFlush("Hello, Netty!");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        System.out.println("Client received: " + msg);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.print

StackTrace();
        ctx.close(); // 关闭连接
    }
}

解释

  1. 服务器端

    • ServerBootstrap:引导服务器的启动。
    • NioEventLoopGroup:处理I/O操作的线程池,bossGroup接受连接,workerGroup处理连接。
    • NioServerSocketChannel:对应于NIO中的ServerSocketChannel
    • ChannelInitializer:初始化新连接的通道,添加处理器。
    • NettyEchoServerHandler:业务处理器,实现了消息的接收和回送。
  2. 客户端

    • Bootstrap:引导客户端的启动。
    • NioSocketChannel:对应于NIO中的SocketChannel
    • NettyEchoClientHandler:客户端业务处理器,实现了消息的发送和接收。

Netty通过这些类和机制,简化了NIO编程,使得实现高性能的网络应用变得更加容易和高效。

总结

IO和NIO在Java中提供了不同的I/O处理模型,适用于不同的应用场景。NIO通过非阻塞I/O和选择器机制,可以高效地处理高并发和大数据量的网络通信。Netty作为基于NIO的高性能网络应用框架,进一步简化了NIO的编程,使得开发高性能网络应用更加便捷。通过实际案例,可以清楚地看到NIO和Netty在处理高并发网络通信中的优势。

标签:netty,NIO,io,区别,IO,new,import,channel
From: https://www.cnblogs.com/irobotzz/p/18216283

相关文章

  • 【C++】旋转字符串——精准与否,就是屠宰和手术的区别
    ✨题目链接:NC114旋转字符串✨题目描述 字符串旋转:给定两字符串A和B,如果能将A从中间某个位置分割为左右两部分字符串(可以为空串),并将左边的字符串移动到右边字符串后面组成新的字符串可以变为字符串B时返回true。例如:如果A=‘youzan’,B=‘zanyou’,A按‘you’‘zan’......
  • 推导2维镜像变换(Reflection Transform)的公式
    我们知道2维的旋转变换公式为Q=(......
  • BeanFactory和FactoryBean区别
    BeanFactoryBeanFactory是SpringIoC容器的核心接口,它定义了IoC容器的基本功能,如管理应用程序组件之间的依赖关系。BeanFactory负责实例化、配置和组装bean。它提供了获取bean实例的方法,通常通过bean的名称或类型来获取。BeanFactory是SpringIoC容器最底层的实现......
  • TypeScript中的`let`、`const`、`var`区别:变量声明的规范与实践
    TypeScript中的let、const、var区别:变量声明的规范与实践引言在TypeScript中,变量声明是代码编写的基础部分。let、const、var是三种用于变量声明的关键字,它们各自有不同的作用域规则和可变性特点。基础知识作用域:变量可以在整个文件(全局作用域)或某个特定代码块(局部作用......
  • 接口报错.w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework
    1、报文:.w.s.m.s.DefaultHandlerExceptionResolver:Resolved[org.springframework.http.converter.HttpMessageNotReadableException:JSONparseerror:Unexpectedcharacter('''(code39)):wasexpectingdouble-quotetostartfieldname;nestedex......
  • 阿里重排论文PRM 《Personalized Re-ranking for Recommendation》
    和DLCM做法类似,都是使用序列模型对rank后的结构做rerank,不同点是PRM使用了transformencoder来建模,并且使用了用户预训练向量和位置向量最后一层使用了softmax来计算每个item被点击的概率(论文提到使用click作为label,也就是所存在多个label为1的情况,不知道有没有做什么特殊处理),并......
  • TransactionTemplate编程式事务的使用
    TransactionTemplate在Spring框架中,TransactionTemplate是一个用于编程式事务管理的工具类。它提供了一种在代码中显式控制事务边界的方式,使开发人员可以在方法级别定义事务的开始和结束点。TransactionTemplate简化了事务管理的操作,同时提供了一些附加功能,如事务传播行为和异常......
  • 淘宝扭蛋机与盲盒小程序的区别是什么
    淘宝扭蛋机与盲盒小程序在核心理念上均为用户提供了有趣且充满惊喜的购物体验,但在具体实现和功能上存在一些区别。以下是它们之间的主要区别:商品选择与展示:淘宝扭蛋机小程序主要模拟了真实的扭蛋机体验,提供了丰富多样的扭蛋商品,涵盖了动漫、游戏、影视、明星等各个领域。......
  • github 解决推拉代码提示 REMOTE HOST IDENTIFICATION HAS CHANGED 失败
    1.背景在拉取github上一个新项目的时候爆出WARNING:REMOTEHOSTIDENTIFICATIONHASCHANGED!第一反应是电脑被黑了,传说中的中间人攻击(题外话一下,其实所有的代理软件都算是中间人哦~),稍微检查了一下,应该不是。仔细看内容,有点意思。@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@......
  • 阿里面试:NIO为什么会导致CPU100%?
    在Java中总共有三种IO类型:BIO(BlockingI/O,阻塞I/O)、NIO(Non-blockingI/O,非阻塞I/O)和AIO(AsynchronousI/O,异步I/O),它们的区别如下:在JDK1.4之前,只有BIO一种模式,其开发过程相对简单,新来一个连接就会创建一个新的线程处理,但随着请求并发度的提升,BIO很快遇到了性能瓶颈。......