Java〖NIO上篇〗看这一篇就够了 缓冲区 通道
程序员文章站
2022-05-07 21:47:15
...
Java NIO
PS: 之前一直想了解这个NIO到底是什么东西,奈何目前用不到,听说现在许多框架都在用,而且面试的时候也有被问道,感觉还是去多了解了解底层怎么实现的~~这是我的第100篇博客!!!
一. NIO与IO区别
NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector
二. 缓冲区
缓冲区(Buffer) :一个用于特定基本数据类型的容器。由 java.nio 包定义的,所有缓冲区都是 Buffer 抽象类的子类。
Java NIO 中的 Buffer 主要用于与 NIO 通道进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的。
2.1 直接缓冲区与非直接缓冲区
- 非直接缓冲区:通过allocate()方法分配缓冲区,将缓冲区建立在JVM的内存中
- 直接缓冲区:通过allocateDirect()方法分配直接缓冲区,将缓冲区建立在物理内存中。可以提高效率
这里探究验证了一下底层Buffer的原理
//分配指定大小的缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
String s=new String("abcde");
//缓冲区存数据
System.out.println("PUT操作");
byteBuffer.put(s.getBytes());
System.out.println(byteBuffer.position()); //5
System.out.println(byteBuffer.limit()); //1024
System.out.println(byteBuffer.capacity()); //1024
System.out.println("***************************");
System.out.println("filp操作");
byteBuffer.flip();
System.out.println(byteBuffer.position()); //0
System.out.println(byteBuffer.limit()); //5
System.out.println(byteBuffer.capacity()); //1024
System.out.println("***************************");
System.out.println("get操作");
byte[] b=new byte[byteBuffer.limit()];
byteBuffer.get(b,0,4);
if(byteBuffer.hasRemaining()){ //判断缓冲区是否有剩余
System.out.println("缓冲区还有几个剩余的: "+byteBuffer.remaining());
}
System.out.println(byteBuffer.position()); //5
System.out.println(byteBuffer.limit()); //5
System.out.println(byteBuffer.capacity()); //1024
System.out.println("***************************");
System.out.println("rwind操作可重复读"); //又把position置于开头
byteBuffer.rewind();
System.out.println(byteBuffer.position()); //0
System.out.println(byteBuffer.limit()); //5
System.out.println(byteBuffer.capacity()); //1024
System.out.println("***************************");
System.out.println("mark操作记录当前的position位置"); //又把position置于开头
byteBuffer.mark();
byte[] bytes=new byte[1024];
byteBuffer.get(bytes,0,2); //取走前两个,position为2
System.out.println("取走前两个,position为"+byteBuffer.position()); //2
byteBuffer.reset(); //恢复到mark位置的position 为0
System.out.println("恢复到mark位置的position 为"+byteBuffer.position()); //0
System.out.println("***************************");
System.out.println("clear操作清空(非真清空,相当于把position和limit恢复开始状态)数组中的数据依然存在"); //又把position置于开头
byteBuffer.clear();
System.out.println(byteBuffer.position()); //0
System.out.println(byteBuffer.limit()); //1024
System.out.println(byteBuffer.capacity()); //1024
System.out.println("***************************");
//获取第一个bytez转化为char还是能输出
System.out.println("获取第一个bytez转化为char还是能输出"+(char)byteBuffer.get());
if(byteBuffer.hasRemaining()){ //判断缓冲区是否有剩余
System.out.println("缓冲区还有几个剩余的: "+byteBuffer.remaining());
}
ByteBuffer byteBuffer=ByteBuffer.allocateDirect(1024);//创建一个直接缓冲区
System.out.println(byteBuffer.isDirect());//判断是否为直接缓冲区
三. 通道
通道:由java.nio.channels包定义。
Channel表示IO源与目标打开的连接。
Channel类似于传统的“流”。但其自身不能直接访问数据,Channel只能与Buffer进行交互。
java.nio.channels.Channel 接口
-
FileChannel:用于读取、写入、映射和操作文件的通道。
-
SocketChannel:通过 TCP 读写网络中的数据。
-
ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel。
-
DatagramChannel:通过 UDP 读写网络中的数据通道。
3.1 java针对支持通道的类提供了getChannel()方法
- 本地IO:
FileInputStream/FileOutputStream
long start=System.currentTimeMillis();
FileInputStream fis=null;
FileOutputStream fos=null;
//获取通道
FileChannel inchannel = null;
FileChannel outchannel = null;
try {
fis = new FileInputStream("E:\\image/1.jpg");
fos = new FileOutputStream("E:\\image/3.jpg");
inchannel = fis.getChannel();
outchannel = fos.getChannel();
//设置非直接缓冲区大小
ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
while(inchannel.read(byteBuffer)!=-1){ //将通道里的数据存入缓冲区
byteBuffer.flip(); //切换读取模式;
outchannel.write(byteBuffer); // //将缓冲区中的数据写入通道中
byteBuffer.clear(); ////清空缓冲区
}
}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();
}
}
}
long end=System.currentTimeMillis();
System.out.println("耗费时间:"+(end-start));//耗费时间:1094
RandomAccessFile
RandomAccessFile raf=new RandomAccessFile("E:\\image/1234.txt","rw");
//建立通道
FileChannel inChannel = raf.getChannel();
//创建缓存区数组 ->分散读取
ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
ByteBuffer byteBuffer1=ByteBuffer.allocate(100);
ByteBuffer[] buffers={byteBuffer,byteBuffer1};
//读取到缓存区
inChannel.read(buffers);
for (ByteBuffer buffer : buffers) {
buffer.flip(); //切换为读模式
}
//查看各自通道有多少
System.out.println(new String(buffers[0].array(),0,buffers[0].limit()));
System.out.println("****************************************************");
System.out.println(new String(buffers[1].array(),0,buffers[1].limit()));
//聚集写入
RandomAccessFile raf2=new RandomAccessFile("E:\\image/12345.txt","rw");
FileChannel outChannel = raf2.getChannel();
outChannel.write(buffers);
inChannel.close();
outChannel.close();
- 网络IO:
Socket
ServerSocket
DatagramSocket
3.2 在JDK 1.7 中的NIO.2 针对各个通道提供了静态方法 open()
long start=System.currentTimeMillis();
//通过静态方法 open()创建通道
FileChannel inChannel=FileChannel.open(Paths.get("E:\\image/1.jpg"), StandardOpenOption.READ); //读模式
FileChannel outChannel=FileChannel.open(Paths.get("E:\\image/4.jpg"),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[] bytes=new byte[inMappedBuf.limit()];
inMappedBuf.get(bytes);
outMappedBuf.put(bytes);
//关闭通道
inChannel.close();
outChannel.close();
long end=System.currentTimeMillis();
System.out.println("耗时: "+(end-start));
直接通过通道复制
long start=System.currentTimeMillis();
FileChannel inChannel=FileChannel.open(Paths.get("E:\\image/1.jpg"), StandardOpenOption.READ); //读模式
FileChannel outChannel=FileChannel.open(Paths.get("E:\\image/4.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE); //读写模式;存在就覆盖,不存在就创建
inChannel.transferTo(0,inChannel.size(),outChannel);
inChannel.close();
outChannel.close();
long end=System.currentTimeMillis();
System.out.println("耗时: "+(end-start));