JAVA NIO实现的类库概述

JAVA NIO

NIO库是在JDK 1.4中引入的。JDK1.7升级了NIO类库,升级后的NIO类库被称为NIO2.0。在这篇文章中,我们只介绍JDK中的NIO2。NETTY其实就是基于NIO2(注意有部分是自己实现的,比如说对epoll的使用)。https://stackoverflow.com/questions/23465401/why-native-epoll-support-is-introduced-in-netty
以下的NIO均指NIO2。
JAVA NIO会根据操作系统的种类和版本决定使用哪一种系统调用来实现NIO。对于Linux,常见的有epoll,poll,select。在Linux 2.6+版本,Java NIO采用的epoll(即EPollSelectorImpl类),对于2.4.x的,则使用poll(即PollSelectorImpl类)。

NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector。传统IO基于字节流和字符流进行操作,而NIO基于Channel和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择区)用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个线程可以监听多个数据通道。

使用NIO一定要注意NIO臭名昭著的epoll空轮训BUG。selector的轮训(select)结果为空的情况下,则就相当于while循环满载,会迅速耗尽CPU资源。Netty对这个BUG进行了简单解决,就是对某段时间内的空轮训进行计数,如果超过设定值则会重建select,将原有的Channel从旧的Selector上去除注册,重新注册到新的Selector上,并将原来的Selector关闭。

https://www.cnblogs.com/JAYIT/p/8241634.html

类图

这里写图片描述
通过Channel我们可以实现零拷贝,下图中1,3是DMA传输。2就是MMAP。这是使用native方法

sun.nio.ch.FileChannelImpl#transferTo0

在这里插入图片描述

这里写图片描述
DirectByteBuffer内部采用unsafe类进行分配内存

    DirectByteBuffer(int cap) {                   // package-private

        super(-1, 0, cap, cap);
        boolean pa = VM.isDirectMemoryPageAligned();
        int ps = Bits.pageSize();
        long size = Math.max(1L, (long)cap + (pa ? ps : 0));
        Bits.reserveMemory(size, cap);

        long base = 0;
        try {
            base = unsafe.allocateMemory(size);

这里写图片描述

基本用法

        System.out.println("Listening on port " + port);
        ServerSocketChannel serverChannel = ServerSocketChannel.open();// 打开一个未绑定的serversocketchannel
        ServerSocket serverSocket = serverChannel.socket();// 得到一个ServerSocket去和它绑定 
        Selector selector = Selector.open();// 创建一个Selector供下面使用
        serverSocket.bind(new InetSocketAddress(port));//设置server channel将会监听的端口
        serverChannel.configureBlocking(false);//设置非阻塞模式
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);//将ServerSocketChannel注册到Selector
        while (true) 
        {
            // This may block for a long time. Upon returning, the
            // selected set contains keys of the ready channels.
            int n = selector.select();
            if (n == 0) 
            {
                continue; // nothing to do
            }           
            java.util.Iterator<SelectionKey> it = selector.selectedKeys().iterator();// Get an iterator over the set of selected keys
            //在被选择的set中遍历全部的key
            while (it.hasNext()) 
            {
                SelectionKey key = (SelectionKey) it.next();
                // 判断是否是一个连接到来
                if (key.isAcceptable()) 
                {
                    ServerSocketChannel server =(ServerSocketChannel) key.channel();
                    SocketChannel channel = server.accept();
                    registerChannel(selector, channel,SelectionKey.OP_READ);//注册读事件
                    sayHello(channel);//对连接进行处理
                }
                //判断这个channel上是否有数据要读
                if (key.isReadable()) 
                {
                    readDataFromSocket(key);
                }
                //从selected set中移除这个key,因为它已经被处理过了
                it.remove();
            }
        }

AIO

其实就是返回了一个future。

   //AsynchronousServerSocketChannel.java
    public abstract Future<AsynchronousSocketChannel> accept();

AsynchronousSocketChannel.java

    @Override
    public abstract Future<Integer> read(ByteBuffer dst);
    @Override
    public abstract Future<Integer> write(ByteBuffer src);

和Netty对比

一般认为,Netty比NIO2要强。spark/kafka使用的是NIO,但是dubbo使用的是netty。老师讲,二者在原理上并没有什么本质区别,但是只不过Netty BUG更少,功能更稳定,使用更简单。个人觉得,国外框架代码不愿意使用netty,主要是尽量减少对其他框架的依赖的考虑。

典型使用

我们看下kafka-client对NIO selector的使用好了

    //对server进行连接
    public void connect(String id, InetSocketAddress address, int sendBufferSize, int receiveBufferSize) throws IOException {
        SocketChannel socketChannel = SocketChannel.open();
        ...
        //注册到nioSelector上
        SelectionKey key = socketChannel.register(nioSelector, SelectionKey.OP_CONNECT);



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值