NIO:通道Channel讲解

      了解了缓冲区后,下来需要了解真正传输数据的通道Channel,Channel是做什么用的?再来简单回顾一下,通道顾名思义就是传递的介质,Channel就类似于传统IO中的流,是直接对接操作系统的,当我们处理好缓冲区后,就可以通过缓冲区对通道进行数据的输入输出,完成整个NIO的过程。
      Java为Channel接口提供了DatagtamChannel、FileChannel、Pipe.SinkChannel、ServerSocketChannel、SocketChannel等实现类,这些通道是根据功能来划分的,例如Pipe.SinkChannel和Pipe.SourceChannel是用于支持线程之间通信的管道,ServerSocketChannel,SocketChannel是用于支持TCP网络通信的,而DatagramChannel则是用于支持UDP网络通信的通道。

NIO使用通道进行数据读写有两种方式:
1.通过内存映射
2.通过传统IO模式,通过多次读写
通过内存映射是将数据映射到内存空间中,这样的方式明显效率是很高的,如果Channel对应的文件过大,使用映射一次将所有的文件内容映射到内存中会引起性能的下降,就可以使用第二种方式,使用传统的IO方式分多次进行

比较几种读写方式,效率从高到低是:
NIO内存映射 > NIO多次读写 > 传统IO使用缓存 > 使用普通IO

下面的两个例子都用FileChannel来说明

内存映射

Channel提供了map()方法来完成内存映射的过程,该过程会返回一个MappedByteBuffer,这个类是ByteBuffer的子类,也是一个缓存。
在这里插入图片描述
我们可以直接通过操作缓存来进行通道数据的交换

public class ChannelTest {
    public static void main(String[] args) {
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try {
        	//获得通道
            inChannel = new FileInputStream("e:\\txt\\beauty.jpeg").getChannel();
            //为什么不是OutputStream???
            outChannel = new RandomAccessFile("e:\\beauty.jpeg","rw").getChannel();
            long size = inChannel.size();
            //映射为缓存
            MappedByteBuffer inBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, size);
            MappedByteBuffer outBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, size);
            for (int i = 0; i < size; i++) {
                outBuffer.put(inBuffer.get(i));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        		//关闭通道
                try {
                    if(inChannel!=null) {
                        inChannel.close();
                    }
                    if(outChannel!=null) {
                        outChannel.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }
}

这个demo实现一个图片的复制,这块用到的通道是FileChannel。
观察demo,会有一个为什么读入文件通道是FileInputStream,但是写入为什么用的是RandomAccessFile?
这是因为FileChannel和SocketChannel不同,他是一个单向的通道,虽然有write方法,但是如果调用write就会抛出一个NonWritableChannelException 异常,这是因为他们都实现了ByteChannel,ByteChannel同时有read和write方法。如果用FileOutputStream映射的模式确没有WRITE_NOLY,所以只能用RandomAccessFile这种可以读,也可以写的流来获得通道,这样只用其中写的功能就可以完成了。

多次读写

public class ChannelTest2 {
    public static void main(String[] args) {
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try {
            inChannel = new FileInputStream("e:\\beauty.jpeg").getChannel();
            outChannel = new FileOutputStream("e:\\beauty2.jpeg").getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            while((inChannel.read(buffer))!=-1) {
                buffer.flip();
                outChannel.write(buffer);
                buffer.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(inChannel!=null) {
                    inChannel.close();
                }
                if(outChannel!=null) {
                    outChannel.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

这种多次读写的方式和普通IO流非常相似,以前是一个缓冲数组,现在是一个缓冲区,都是分多次读入,然后再写到指定流中。

猜你喜欢

转载自blog.csdn.net/weixin_42220532/article/details/90489887