了解了缓冲区后,下来需要了解真正传输数据的通道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这种可以读,也可以写的流来获得通道,这样只用其中写的功能就可以完成了。
这种多次读写的方式和普通IO流非常相似,以前是一个缓冲数组,现在是一个缓冲区,都是分多次读入,然后再写到指定流中。
