企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] # 通道(Channel) 通道表示打开到 IO 设备(例如:文件、套接字)的连接。 若需要使用 NIO 系统,需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区。 然后操作缓冲区,对数据进行处理。Channel 负责传输, Buffer 负责存储。 通道是由 java.nio.channels 包定义的。 Channel 表示 IO 源与目标打开的连接。 Channel 类似于传统的“流”。只不过 Channel本身不能直接访问数据, Channel 只能与Buffer 进行交互。 **通道都是操作缓存区完成全部的功能的** ## Java中所有已知 Channel 实现类: * AbstractInterruptibleChannel * AbstractSelectableChannel * DatagramChannel * FileChannel * Pipe.SinkChannel * Pipe.SourceChannel * SelectableChannel * ServerSocketChannel * SocketChannel 常用的有入下几个: * FileChannel:用于读取、写入、映射和操作文件的通道,处理本地文件 * DatagramChannel:通过 UDP 读写网络中的数据通道。 * SocketChannel:通过 TCP 读写网络中的数据。 * ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel。 ## 获取通道 虽然FileChannel即可以读取,也可以写入.但是FileInputStream获取的FileChannel只能读,FileOutputStream获取的FileChannel只能写. RandomAccessFile获取的FileChannel是只读的还是读写的Channel,取决于RandomAccessFile打开文件的模式 **方式一** 对支持通道的对象调用getChannel() 方法。支持通道的类如下: * FileInputStream * FileOutputStream * RandomAccessFile * DatagramSocket * Socket * ServerSocket **方式二** 获取通道的其他方式是使用 Files 类的静态方法 newByteChannel() 获取字节通道。 **方式三** 通过通道的静态方法 open() 打开并返回指定通道。 ## 常用方法 * 分散(scatter)地从 Channel 中读取是将数据读入多个 Buffer 的操作。 因此,通道将来自通道的数据“分散”到多个缓冲区中。 * 聚集(gather)地写入 Channel 是将来自多个缓冲区的数据写入单个通道的操作。 因此,通道将来自多个缓冲区的数据“收集”到同一个通道中。 ![](https://img.kancloud.cn/14/f4/14f4c3e25dde4ce6a1bf7a10d65780ae_968x517.png) ## FileChannel 为了更形象解释说明的Channel,下面准备以FileChannel的一些简单代码进行说明就容易懂了。 准备以FileOutputStream类为准,这两个类都是支持通道操作的。 ~~~ String info[] = {"aa","xx"} ; File file = new File("d:" + File.separator + "testfilechannel.txt") ; FileOutputStream output = null ; FileChannel fout = null; try { output = new FileOutputStream(file) ; fout = null; fout = output.getChannel() ; // 得到输出的通道 ByteBuffer buf = ByteBuffer.allocate(1024) ; for(int i=0;i<info.length;i++){ buf.put(info[i].getBytes()) ; // 字符串变为字节数组放进缓冲区之中 } buf.flip() ; fout.write(buf) ; // 输出缓冲区的内容 } catch (Exception e) { e.printStackTrace(); }finally{ if(fout!=null){ try { fout.close() ; } catch (IOException e) { e.printStackTrace(); } } if(output!=null){ try { output.close() ; } catch (IOException e) { e.printStackTrace(); } } } ~~~ # DMA和channel 在DMA模式下,cpu只需要向DMA控制器下达指令,让DMA控制器来处理数据的传送,数据传送完毕再把信息反馈给cpu,这样很大程度能降低cpu的资源占用率,可以大大节省系统资源. DMA模式又分为Single-Word(单字节)和Multi-Word(多字节)两种,其中能达到最大传输效率也只有16.6MB/S DMA大量发送消息,会有DMA总线冲突 ![](https://img.kancloud.cn/58/e1/58e152258c691b1aee720e3870cb6267_856x422.png) channel替代DMA ![](https://img.kancloud.cn/a9/68/a968a421d7458a81102daf4a640e20be_974x502.png) # 直接缓冲区和非直接缓冲区实现文件复制 ## 直接缓冲区 ~~~ @Test public void test2(){ FileChannel inChannel = null; FileChannel outChannel = null; try { long start = System.currentTimeMillis(); // String srcPath = "Dilraba.jpg"; // String destPath = "Dilraba3.jpg"; String srcPath = "C:\\Users\\Administrator\\Desktop\\score\\战狼.mp4"; String destPath = "C:\\Users\\Administrator\\Desktop\\score\\战狼1.mp4"; //实例化Channel inChannel = FileChannel.open(Paths.get(srcPath), StandardOpenOption.READ); outChannel = FileChannel.open(Paths.get(destPath), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE); //得到直接缓冲区 MappedByteBuffer inMappedBuffer = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());//size():返回操作的文件的大小 MappedByteBuffer outMappedBuffer = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size()); //数据的读写操作 byte[] buffer = new byte[inMappedBuffer.limit()]; inMappedBuffer.get(buffer); outMappedBuffer.put(buffer); long end = System.currentTimeMillis(); System.out.println("直接缓冲区花费的时间:" + (end - start));//1929-1894 } catch (IOException e) { e.printStackTrace(); }finally{ if(inChannel != null){ try { inChannel.close(); } catch (IOException e) { e.printStackTrace(); } } if(outChannel != null){ try { outChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } } ~~~ ## 非直接缓冲区 ~~~ @Test public void test1(){ FileChannel inChannel = null; FileChannel outChannel = null; FileInputStream fis = null; FileOutputStream fos = null; try { long start = System.currentTimeMillis(); // String srcPath = "Dilraba.jpg"; // String destPath = "Dilraba2.jpg"; String srcPath = "C:\\Users\\Administrator\\Desktop\\score\\战狼.mp4"; String destPath = "C:\\Users\\Administrator\\Desktop\\score\\战狼2.mp4"; fis = new FileInputStream(srcPath); fos = new FileOutputStream(destPath); //实例化Channel inChannel = fis.getChannel(); outChannel = fos.getChannel(); //提供ByteBuffer ByteBuffer buffer = ByteBuffer.allocate(1024); while(inChannel.read(buffer) != -1){ buffer.flip();//修改为读数据模式 outChannel.write(buffer); buffer.clear();//清空 } long end = System.currentTimeMillis(); System.out.println("非直接缓冲区花费的时间:" + (end - start));//20795-13768 } catch (Exception e) { e.printStackTrace(); }finally{ if(outChannel != null){ //关闭资源 try { outChannel.close(); } catch (IOException e) { e.printStackTrace(); } } if(inChannel != null){ try { inChannel.close(); } catch (IOException e) { e.printStackTrace(); } } if(fos != null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if(fis != null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } } ~~~ # transferTo和transferFrom 将数据从源通道传输到其他Channel中 ~~~ FileChannel inChannel = FileChannel.open(Paths.get("Dilraba.jpg"), StandardOpenOption.READ); FileChannel outChannel = FileChannel.open(Paths.get("mm1.jpg"), StandardOpenOption.WRITE,StandardOpenOption.CREATE); //transferTo():将数据从可读的Channel中转换到可写的Channel中 // inChannel.transferTo(0, inChannel.size(), outChannel); //transferFrom():将数据从可读的Channel中转换到可写的Channel中 outChannel.transferFrom(inChannel, 0, inChannel.size()); inChannel.close(); outChannel.close(); ~~~ # 分散(Scatter)读取和聚集(Gather)写入 * 分散读取(Scattering Reads)是指从Channel中读取的数据分散到多个Buffer中 ![](https://img.kancloud.cn/ee/25/ee254d0bdef8e2e7067ac8df7721a6b5_600x371.png) 注意: 按照缓冲区的顺序,从channel中读取的数据依次将Buffer填满 * 聚集写入(Gathering Writes)是指将多个Buffer中的数据聚集到Channel ![](https://img.kancloud.cn/90/1a/901ab005e3b4094533f66999d1465c09_630x351.png) 注意: 按照缓冲区的顺序,写入position和limit之间的数据到Channel ~~~ RandomAccessFile readRaf = new RandomAccessFile("EclipseKeys.java", "r"); RandomAccessFile writeRaf = new RandomAccessFile("EclipseKeys1.java", "rw"); //实例化Channel FileChannel inChannel = readRaf.getChannel(); FileChannel outChannel = writeRaf.getChannel(); ByteBuffer buffer1 = ByteBuffer.allocate(1024); ByteBuffer buffer2 = ByteBuffer.allocate(2048); ByteBuffer[] dsts = {buffer1,buffer2}; inChannel.read(dsts);//分散读取 //改为可读模式 buffer1.flip(); buffer2.flip(); System.out.println(new String(buffer1.array(),0,buffer1.limit())); System.out.println(); System.out.println(new String(buffer2.array(),0,buffer2.limit())); //测试聚集写入 outChannel.write(dsts); outChannel.close(); inChannel.close(); ~~~