Channel(通道)
定义
Channel(通道)定义在java.nio.channels包中。Channel表示IO源与目标打开的连接。Channel类似于传统的“流”,但Channel本身不能直接访问数据,只能与Buffer进行交互。
由来
早期的IO由CPU负责,由于CPU与物理磁盘之间存在巨大的运算与传输速率差距,导致CPU利用率不高。于是有了DMA来单独处理IO部分。但是当应用程序有大量的IO请求时,又会造成总线冲突的问题,于是将DMA更新成了通道的方式。通道与DMA的区别在于,当有IO操作时,DMA需要向CPU请求获取总线的控制权,进行IO操作,然后将总线的控制权归还CPU;通道则是一个完全独立的处理器,无需向CPU申请总线的控制权,专门用于IO操作。

Channel接口的主要实现类
- FileChannel :用于读取、写入、映射和操作文件的通道。
- DatagramChannel:通过UDP读写网络中的数据通道。
- SocketChannel:通过TCP读写网络中的数据。
- ServerSocketChannel:可以监听新进来的TCP连接,对每一个新进来的连接都会创建一个SocketChannel。
获取通道
- Java针对支持通道的类提供了getChannel()方法:
本地IO:
- FileInputStream/FileOutputStream
- RandomAccessFile
网络IO: - Socket
- ServerSocket
- DatagramSocket
利用通道完成文件的复制的代码如下:
@Test
public void test1() throws IOException {
FileInputStream fis =new FileInputStream("1.png");
FileOutputStream fos = new FileOutputStream("2.png");
//①获取通道
FileChannel inChannel = fis.getChannel();
FileChannel outChannel = fos.getChannel();
//②分配指定大小的缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);
//③将通道中的数据存入缓冲区中
while (inChannel.read(buf) != -1) {
buf.flip();//切换到读模式
//⑤将缓冲区中的数据写入通道中
outChannel.write(buf);
buf.clear();//清空缓冲区
}
//关闭连接,释放资源
inChannel.close();
outChannel.close();
fis.close();
fos.close();
}
2.在JDK1.7中的NIO.2针对各个通道提供了静态方法open()
使用直接缓冲区完成文件的复制(内存映射文件)代码如下:
@Test
public void test2() throws IOException {
FileChannel inChannel = FileChannel.open(Paths.get("1.png"),
StandardOpenOption.READ); //打开源文件读通道
FileChannel outChannel = FileChannel.open(Paths.get("3.png"),
StandardOpenOption.WRITE,
StandardOpenOption.READ,
StandardOpenOption.CREATE);//打开一个目标文件通道,如果没有则创建,否则覆盖,并赋予读写权限
//内存映射文件
MappedByteBuffer inMappedBuf = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());//读通道映射到直接直接内存中
MappedByteBuffer outMappedBuf = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());//写通道映射到直接内存中
//直接对缓冲区进行数据的读写操作
byte[] dst = new byte[inMappedBuf.limit()];
inMappedBuf.get(dst);
outMappedBuf.put(dst);
inChannel.close();
outChannel.close();
}
也可直接使用transferFrom或transferTo通过直接缓冲区(不需要在用户代码中声明)直接传递,并且效率更高。但这样也会增加系统的开销,只有借助垃圾回收机制才能对直接缓冲区资源进行回收。
@Test
public void test3() throws IOException {
FileChannel inChannel = FileChannel.open(Paths.get("1.png"),
StandardOpenOption.READ); //打开源文件读通道
FileChannel outChannel = FileChannel.open(Paths.get("4.png"),
StandardOpenOption.WRITE,
StandardOpenOption.READ,
StandardOpenOption.CREATE);//打开一个目标文件通道,如果没有则创建,否则覆盖,并赋予读写权限
//两个通道之间直接传输数据进行回收
// inChannel.transferTo(0, inChannel.size(), outChannel);
outChannel.transferFrom(inChannel, 0, inChannel.size());
inChannel.close();
outChannel.close();
}
分散(Scatter)与聚集(Gather)
-
分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中。(按照缓冲区顺序,从Channel中读取的数据一次将Buffer填满)。
分散读取
-
聚集写入(Gathering Writes):将多个缓冲区中的数据按照顺去聚集写入到通道中。
聚集写入
示例代码如下:
@Test
public void test4() throws IOException {
RandomAccessFile raf1 = new RandomAccessFile("1.txt", "rw");
//1.获取通道
FileChannel inChannel = raf1.getChannel();
//2.分配指定大小的缓冲区
ByteBuffer buf1 = ByteBuffer.allocate(100);
ByteBuffer buf2 = ByteBuffer.allocate(1024);
//3.分散读取
ByteBuffer[] bufs = {buf1, buf2};
inChannel.read(bufs);
for (ByteBuffer byteBuffer : bufs) {
byteBuffer.flip();
}
System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));
System.out.println("-------------------------------");
System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));
//4.聚集写入
RandomAccessFile raf2 = new RandomAccessFile("2.txt", "rw");
FileChannel outChannel = raf2.getChannel();
outChannel.write(bufs);
}
网友评论