美文网首页Java 杂谈
JAVA NIO编程入门(一)

JAVA NIO编程入门(一)

作者: 木木匠 | 来源:发表于2018-10-07 21:44 被阅读0次

    一、前言

    笔者之前接触的NIO编程比较少,所以对这一块的基础也比较弱,NIO作为java编程中一个重要的模块,不能很好的掌握它,感觉自己在java方面就掌握的不够,所以,接下来,笔者会学习NIO编程,所以,该系列文章不会涉及到很深源码解析,纯粹的是学习课程,也可以理解为笔者的笔记,记录学习NIO的过程,同时也希望这类文章可以对同样想掌握NIO编程的你有帮助。

    二、什么是NIO?

    Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java 1.4开始),Java NIO提供了与标准IO不同的IO工作方式。NIO可以理解为非阻塞IO,传统的IO的read和write只能阻塞执行,线程在读写IO期间不能干其他事情,比如调用socket.read()时,如果服务器一直没有数据传输过来,线程就一直阻塞,而NIO中可以配置socket为非阻塞模式。

    三、IO和NIO的区别

    • IO是面向字节流和字符流的,而NIO是面向缓冲区的。
    • IO是阻塞模式的,NIO是非阻塞模式的
    • NIO新增了选择器的概念,可以通过选择器监听多个通道。

    四、NIO相关概念

    Channel(通道)

    Java NIO的通道类似流,但又有些不同:

    既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。
    通道可以异步地读写。

    通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入。

    image

    Channel的实现

    这些是Java NIO中最重要的通道的实现:

    • FileChannel: 从文件中读写数据。

    • DatagramChannel : 能通过UDP读写网络中的数据。

    • SocketChannel: 能通过TCP读写网络中的数据。

    • ServerSocketChannel :可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。

    Buffer(缓冲区)

    缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。

    Buffer(缓冲区)的主要属性

    属性 功能
    capacity 容量
    position 缓冲区当前位置指针,最大可为capacity – 1
    limit 缓冲区最大读取和写入限制

    buffer属性示意图:

    image

    上图展示了写模式和读模式下,以上属性的示意图,写模式下,limit和capacity是一样的,这表示你能写入的最大容量数据,读模式下,limit会和position一样,表示你能读到写入的全部数据。

    Buffer(缓冲区)的主要分类

    • ByteBuffer
    • MappedByteBuffer
    • CharBuffer
    • DoubleBuffer
    • FloatBuffer
    • IntBuffer
    • LongBuffer
    • ShortBuffer

    其实以上基本上为了接收不同的数据类型而对应的,只有一个特殊的MappedByteBuffer,本次先不学习它,后续再去了解它。

    五、实战

    我们主要以理解上面介绍的概念为目的实现一个简单的NIO编程,读取文件夹内的文件,然后输出到控制台。

     public static  void testNio(){
            try {
                RandomAccessFile rdf=new RandomAccessFile("E:\\nio\\niotest.txt","rw");
                //利用channel中的FileChannel来实现文件的读取
                FileChannel inChannel=  rdf.getChannel(); 
                //设置缓冲区容量为10
                ByteBuffer buf=  ByteBuffer.allocate(10);
                //从通道中读取数据到缓冲区,返回读取的字节数量
                int byteRead=inChannel.read(buf);
                //数量为-1表示读取完毕。
                while (byteRead!=-1){
                    //切换模式为读模式,其实就是把postion位置设置为0,可以从0开始读取
                    buf.flip();
                    //如果缓冲区还有数据
                    while (buf.hasRemaining()){
                        //输出一个字符
                        System.out.print((char) buf.get());
                    }
                    //数据读完后清空缓冲区
                    buf.clear();
                    //继续把通道内剩余数据写入缓冲区
                    byteRead = inChannel.read(buf);
                }
                //关闭通道
                rdf.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    

    我在磁盘中niotest.txt的内容是hello world,我们看看运行结果是不是hello world。

    image
    结果和预期一样,我们完成了NIO编程输出磁盘文件内容,其实上面代码中,我第一印象有疑问的地方就是buf.flip(),其他只要用过IO的应该都能理解,既然不能理解我们就先调试下,注释掉该段内容,看看输出结果如何。 image

    注释掉buf.flip()后,输出结果果真不对了,那究竟是为啥呢?

    image
    上图是第一个循环,我们可以看到缓冲区的position=10,limit=10,cap=10。调用buf.hasRemaining()为false,所以buffer第一次没有输出任何东西。看看第二次循环 image

    其实这整个循环可以解析步骤如下:

    • buffer读取了10字节内容,内容就是:hello worl。

    • buf.hasRemaining() 判断为false,直接清空buffer(直接把position复位为0,可以直接覆盖内容),读取剩下内容。

    • 最后只剩下一个字符 d 读取,这个时候读取完后,buffer内容如下:dello worl。

    • 最后输出因为从postion=1位置输出,所以输出:ello worl。

    根据以上解析,我们发现注释了buf.flip()后,position位置写入是什么位置,读出就是什么位置,所以,该方法应该就是把position赋值为0,从开始读取。解读源码也证实了我的猜想,源码如下:

      public final Buffer flip() {
            limit = position;
            position = 0;
            mark = -1;
            return this;
        }
    

    参考

    JAVA NIO

    推荐阅读

    Java锁之ReentrantLock(一)

    Java锁之ReentrantLock(二)

    Java锁之ReentrantReadWriteLock

    相关文章

      网友评论

        本文标题:JAVA NIO编程入门(一)

        本文链接:https://www.haomeiwen.com/subject/zdyraftx.html