• javaNIO:通道和文件通道 Channel


    通道是什么

    通道式(Channel)是java.nio的第二个主要创新。通道既不是一个扩展也不是一项增强,而是全新的、极好的Java I/O示例,提供与I/O服务的直接连接。Channel用于在字节缓冲区和位于通道另一侧的实体(通常是一个文件描述符(window下叫文件句柄)或套接字(网络传输用到,可以理解为网络上的唯一地址))之间有效地传输数据

    通常情况下,通道与操作系统的文件描述符(FileDescriptor)和文件句柄(FileHandler)有着一对一的关系。虽然通道比文件描述符更广义,但开发者经常使用到的多数通道都是连接到开放的文件描述符的。Channel类提供维持平台独立性所需的抽象过程,不然仍然会模拟现代操作系统本身的I/O性能。

    通道是一种途径,借助该途径,可以用最小的总开销来访问操作系统本身的I/O服务。缓冲区则是通道内部用来发送和接收数据的端点,如下图:


    不懂的名词可以参考文件句柄,文件描述符,套接字


    讲完通道,就可以理解io和nio的区别之一 ,在笔记最后也会讲到

    1.传统io是面向流的

    2.nio是面向通道的

    面向流的传统io,在操作同一个文件的时候是需要简历输出流outstream和输入流inputstream两个通道,消耗性能,流是单向的

    nio在操作同一个文件的时候,只创建一个通道channel操作缓冲区buffer,来回写入或读取,更快更优化,通道操作buffer是双向的


    通道基础

    首先,看一下基本的Channel接口,下面是Channel接口的完整源码:

    public interface Channel extends Closeable {
    
        /**
         * Tells whether or not this channel is open.  </p>
         *
         * @return <tt>true</tt> if, and only if, this channel is open
         */
        public boolean isOpen();

        /**
         * Closes this channel.
         *
         * <p> After a channel is closed, any further attempt to invoke I/O
         * operations upon it will cause a {@link ClosedChannelException} to be
         * thrown.
         *
         * <p> If this channel is already closed then invoking this method has no
         * effect.
         *
         * <p> This method may be invoked at any time.  If some other thread has
         * already invoked it, however, then another invocation will block until
         * the first invocation is complete, after which it will return without
         * effect. </p>
         *
         * @throws  IOException  If an I/O error occurs
         */
        public void close() throws IOException;
    
    }

    和缓冲区不同,通道API主要由接口指定。不同的操作系统上通道实现会有根本性的差异,所以通道API仅仅描述了可以做什么,因此很自然地,通道实现经常使用操作系统的本地代码,通道接口允许开发者以一种受控且可移植的方式来访问底层的I/O服务。

    可以从底层的Channel接口看到,对所有通道来说只有两种共同的操作:检查一个通道是否打开isOpen()和关闭一个打开的通道close(),其余所有的东西都是那些实现Channel接口以及它的子接口的类。

    从Channel接口引申出的其他接口都是面向字节的子接口:

    包括WritableByteChannel和ReadableByteChannel。这也正好支持了我们之前的所学:通道只能在字节缓冲区上操作。层次接口表明其他数据类型的通道也可以从Channel接口引申而来。这是一种很好的镭射机,不过非字节实现是不可能的,因为操作系统都是以字节的形式实现底层I/O接口的。

     

    认识通道

    看一下基本的接口:

    public interface ReadableByteChannel extends Channel {
    
        public int read(ByteBuffer dst) throws IOException;
    
    }
    public interface WritableByteChannel
        extends Channel
    {
    
        public int write(ByteBuffer src) throws IOException;
    
    }
    public interface ByteChannel
        extends ReadableByteChannel, WritableByteChannel
    {
    
    }

    通道可以是单向的也可以是双向的。一个Channel类可能实现定义read()方法的ReadableByteChannel接口,而另一个Channel类也许实现WritableByteChannel接口以提供write()方法。实现这两种接口其中之一的类都是单向的,只能在一个方向上传输数据。如果一个类同时实现这两个接口,那么它是双向的,可以双向传输数据,就像上面的ByteChannel。

    通道可以以阻塞(blocking)或非阻塞(nonblocking)模式运行,非阻塞模式的通道永远不会让调用的线程休眠,请求的操作要么立即完成,要么返回一个结果表明未进行任何操作。只有面向流的(stream-oriented)的通道,如sockets和pipes才能使用非阻塞模式

    比方说非阻塞的通道SocketChannel:

    public abstract class SocketChannel
        extends AbstractSelectableChannel
        implements ByteChannel, ScatteringByteChannel, GatheringByteChannel
    {
        ...
    }
    public abstract class AbstractSelectableChannel
        extends SelectableChannel
    {
       ...
    }

    可以看出,socket通道类从SelectableChannel类引申而来,从SelectableChannel引申而来的类可以和支持有条件的选择的选择器(Selectors)一起使用。将非阻塞I/O和选择器组合起来可以使开发者的程序利用多路复用I/O,选择器和多路复用将在之后的文章予以说明。

     

    认识文件通道

    通道是访问I/O服务的导管,I/O可以分为广义的两大类:File I/O和Stream I/O。那么相应的,通道也有两种类型,它们是文件(File)通道和套接字(Socket)通道。文件通道指的是FileChannel,套接字通道则有三个,分别是SocketChannel、ServerSocketChannel和DatagramChannel。

    通道可以以多种方式创建。Socket通道可以有直接创建Socket通道的工厂方法,但是一个FileChannel对象却只能通过在一个打开的RandomAccessFile、FileInputStream或FileOutputStream对象上调用getChannel()方法来获取,开发者不能直接创建一个FileChannel

    文件I/O是我们最常使用的I/O,因此这部分先认识一下文件通道,下一部分再以代码形式演示如何使用文件通道高。用UML图表示一下文件通道的类层次关系:

    文件通道总是阻塞式的,因此不能被置于非阻塞模式下

    前面提到过了,FileChannel对象不能直接创建,一个FileChannel实例只能通过在一个打开的File对象(RandomAccessFile、FileInputStream或FileOutputStream)上调用getChannel()方法获取,调用getChannel()方法会返回一个连接到相同文件的FileChannel对象且该FileChannel对象具有与file对象相同的访问权限,然后就可以使用通道对象来利用强大的FileChannel API了。

    FileChannel对象是线程安全的,阻塞的,多个进程可以在同一个实例上并发调用方法而不会引起任何问题,不过并非所有的操作都是多线程的。影响通道位置或者影响文件的操作都是单线程的,如果有一个线程已经在执行会影响通道位置或文件大小的操作,那么其他尝试进行此类操作之一的线程必须等待,并发行为也会受到底层操作系统或文件系统的影响。

     

    使用文件通道读数据

    讲了这么多理论,下面来看一下如何使用文件通道,首先是从文件中读出数据:

    public static void main(String[] args) throws Exception
    {
        File file = new File("D:/files/readchannel.txt");
        FileInputStream fis = new FileInputStream(file);
        FileChannel fc = fis.getChannel();
        ByteBuffer bb = ByteBuffer.allocate(35);
        fc.read(bb);
        bb.flip();
        while (bb.hasRemaining())
        {
            System.out.print((char)bb.get());
        }
        bb.clear();
        fc.close();
    }

    这是最简单的操作,前面讲过文件通道必须通过一个打开的RandomAccessFile、FileInputStream、FileOutputStream获取到,因此这里使用FileInputStream来获取FileChannel。接着只要使用read方法将内容读取到缓冲区内即可,缓冲区内有了数据,就可以使用前文对于缓冲区的操作读取数据了。文件里面的数据是:

    控制台上打印出来的数据是:

    channel1!
    channel2!
    channel3!

    没有问题。

     

    使用文件通道写数据

    上面看了使用文件通道读数据,接着看一下使用文件通道写数据,差不多:

    public static void main(String[] args) throws Exception
    {
        File file = new File("D:/files/writechannel.txt");
        RandomAccessFile raf = new RandomAccessFile(file, "rw");
        FileChannel fc = raf.getChannel();
        ByteBuffer bb = ByteBuffer.allocate(10);
        String str = "abcdefghij";
        bb.put(str.getBytes());
        bb.flip();
        fc.write(bb);
        bb.clear();
        fc.close();
    }

    这里使用了RandomAccessFile去获取FileChannel,然后操作其实差不多,write方法写ByteBuffer中的内容至文件中,注意写之前还是要先把ByteBuffer给flip一下。

    可能有人觉得这种连续put的方法非常不方便,但是没有办法,所有的NIO通道的读写方法只能使用ByteBuffer(直接内存)

    ps:

    讲到这里,nio和传统io的第一个大的区别就体现出来了

    1.传统io是面向流的

    2.nio是面向通道的

    面向流的传统io,在操作同一个文件的时候是需要简历输出流outstream和输入流inputstream两个通道,消耗性能

    nio在操作同一个文件的时候,只创建一个通道channel操作缓冲区buffer,来回写入或读取,更快更优化

     




  • 相关阅读:
    [转]Linux FTP服务配置
    [转]手把手教你nginx下如何增加网站
    [转]linux一键安装web环境全攻略
    CentOS命令
    [转]Response对象的属性和方法
    beforeRouteLeave vue监听返回的使用方法
    ue项目浏览器出现卡顿及崩溃的原因查找与解决方案
    vue中,解决chrome下,的warning, Added nonpassive event listener to a scrollblocking ‘mousewheel‘ event 问题
    npm
    Java线程池使用案例
  • 原文地址:https://www.cnblogs.com/signheart/p/14442834.html
Copyright © 2020-2023  润新知