首页 > 编程语言 >Java I/O(2):NIO中的Channel

Java I/O(2):NIO中的Channel

时间:2022-10-19 01:00:25浏览次数:48  
标签:Java NIO fos System ByteBuffer new Channel

您好,我是湘王,这是我的博客园,欢迎您来,欢迎您再来~

  

 

为了解决标准Java I/O令人难以忍受的效率问题,从JDK1.4开始,NIO出现了(Non-blocking I/O,官方称之为New I/O)。NIO不但新增加了许多全新的类,而且还对原来的很多类进行了改写。之所以是NIO,是因为使用它的场景众多,譬如开发中必不可少的Tomcat,以及大名鼎鼎的Netty,而Netty更是把NIO发挥到了极致,成为了RPC技术事实上的标准,所以它在JDK1.7中又升级为了AIO(NIO2)。

NIO主要有三大核心部分:

  • Channel(通道)
  • Buffer(缓冲区)
  • Selector(选择器/多路复用器)

传统I/O基于字节流或字符流进行操作,而NIO基于新的Channel和Buffer进行操作。这是它们的比较:

 

 

 

至于原理,不用记,可以这么来理解(我始终秉持的态度是:如果你在大厂是自研类RPC系统或类MQ中间件的,那这个一定要精通;否则理解就好,不必死磕):

可以看到,I/O就像个直肠子,直来直去,对数据流完全是来者不拒,来多少接多少,也不管能不能处理得了,这样极容易造成线程阻塞,也就是电脑卡顿。

而NIO就有点弯弯绕了,它告诉线程:“如果我忙不过来就别等我,你先忙你的”。所以,按照这个约定,如果线程发现它不搭理自己的时候就会去忙别的。不会造成信息堵车。

Channel接口最重要的实现可以分为两大类:用于本地文件和用于网络的Channel。

  • FileChannel:用于本地文件数据的读写
  • DatagramChannel:用于网络UDP数据的读写
  • SocketChannel:客户端用于实现网络TCP数据的读写
  • ServerSocketChannel:服务端用于监听网络TCP的连接请求,每个请求会创建会一个SocketChannel(即客户端连接)

这是和Channel相关的继承结构图:

 

 

 

I/O本就枯燥,如果只是空洞说技术原理就更毫无价值,还是上代码,把NIO和IO比较一下。

创建一个普通的Java项目:

 

 

 

 

 

 

然后随便在网上或者自己电脑上找一个大文件,比如小电影之类的,写这样的代码:

// 把file1中的内容写到file2中去,看看耗时
// I/O读写
long start = System.currentTimeMillis();
try {
    FileInputStream fis = new FileInputStream("你电脑上已经存在的文件路径,例如C:\\file1");
    FileOutputStream fos = new FileOutputStream("你电脑上还不存在的文件路径,例如C:\\file2");
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fis), 1024);
    String line = null;
    while ((line = bufferedReader.readLine()) != null) {
        fos.write(line.getBytes());
    }
    fis.close();
    fos.close();
} catch (IOException e) {
    e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println(end - start);

 

 

然后再稍稍改进一下,看看byte[]相对于BufferedReader的不同:

// 把file1中的内容写到file3中去,看看耗时
// I/O读写(改进)
start = System.currentTimeMillis();
try {
    FileInputStream fis = new FileInputStream("你电脑上已经存在的文件路径,例如C:\\file1");
    FileOutputStream fos = new FileOutputStream("你电脑上还不存在的文件路径,例如C:\\file3");
    byte[] b = new byte[1024];
    int len = 0;
    while ((len = fis.read(b)) != -1) {
        fos.write(b, 0, len);
    }
    fis.close();
    fos.close();
} catch (IOException e) {
    e.printStackTrace();
}
end = System.currentTimeMillis();
System.out.println(end - start);

 

 

最后再用NIO试试看:

// 把file1中的内容写到file4中去,看看耗时
// NIO读写
start = System.currentTimeMillis();
try {
    FileChannel fis = new FileInputStream("你电脑上已经存在的文件路径,例如C:\\file1").getChannel();
    FileChannel fos = new FileOutputStream("你电脑上还不存在的文件路径,例如C:\\file4").getChannel();
    ByteBuffer bytedata = ByteBuffer.allocate(1024);
    while (fis.read(bytedata) != -1) {
        // 读写交叉进行
        bytedata.flip();
        fos.write(bytedata);
        bytedata.clear();
    }
    fis.close();
    fos.close();
} catch (IOException e) {
    e.printStackTrace();
}
end = System.currentTimeMillis();
System.out.println(end - start);

 

 

在main()方法中分别执行这三个方法,看看耗时上有啥不同。尽量找很大的文件,比如几个G的那种。因为现在计算机的配置都比较高,文件太小,一会就读完了,根本看不出来差别。

另外,另外,在NIO中如果一个channel是FileChannel类型的,那么可以直接把FileChannel的数据传输到另一个Channel,就像这样:

 

 

 

SocketChannel、ServerSocketChannel和DatagramChannel的使用也比较简单,就不堆代码了。

Channel还提供了一种被称为Scatter/Gather(分散/聚集)的新功能(也称为Vectored I/O,矢量I/O),它在多个Buffer上实现一个简单的I/O操作。说人话就是:Scatter是把单个Channel的数据发给多个Buffer(分散),而Gather则是把多个Buffer的数据发给单个Channel(聚集),就像这样:

 

 

同样可以用代码来演示一下:

// Scattering reads分散过程
ByteBuffer buffer1 = ByteBuffer.allocate(1024);
ByteBuffer buffer2 = ByteBuffer.allocate(1024);
ByteBuffer[] bufferArray1 = { buffer1, buffer2 };
FileChannel channel1 = new FileInputStream("/testfile1").getChannel();
channel1.read(bufferArray1);

// Gathering writes聚集过程
ByteBuffer buffer3 = ByteBuffer.allocate(1024);
ByteBuffer buffer4 = ByteBuffer.allocate(1024);
ByteBuffer[] bufferArray2 = { buffer1, buffer2 };
FileChannel channel2 = new FileInputStream("/testfile1").getChannel();
channel2.write(bufferArray2);

 

 

好了,NIO也属于Java中比较重要的内容,说多了容易搞晕。慢慢来~

 


 

感谢您抽空品鉴!技术、产品、运营和管理问题,可随时留言私信,欢迎骚扰~

 

标签:Java,NIO,fos,System,ByteBuffer,new,Channel
From: https://www.cnblogs.com/xiangwang1111/p/16804673.html

相关文章

  • Java设计模式 —— 装饰模式
    12装饰模式12.1装饰模式概述DecoratorPattern:动态地给一个对象增加一些额外的职责。提供一种比使用子类更加灵活的方案来扩展功能。装饰模式是一种用于替代继......
  • Java云原生崛起微服务框架Quarkus入门实践
    @目录概述定义GraalVM简介为何使用特性官方性能实战入门示例步骤安装GraalVM创建quarkus工程Idea导入项目Idea运行和调试打包成普通的Jar打包成依赖GraalVM二进制文件打包......
  • java -jar 启动带有参数
    java-Xms512M-Xmx512M-Dserver.port=8700-Dspring.config.location=/iflytek/conf/wm-2.1.0/wm-rest.properties-Deureka.client.serviceUrl.defaultZone=http://10......
  • Java中使用List的add方法后元素相同问题
    在写JavaWeb时,我在后端通过JDBC读取了数据后逐个使用List.add()方法添加元素并通过request方法传给jsp页面解析,但是添加以后出现了在列表里有n个(假设添加了n个元素)最后一个......
  • Java动态绑定机制
    本文主要记录韩顺平老师讲解动态绑定机制过程中提到的重点,本文转载此篇博客【Java】动态绑定机制,并对其进行一定的补充,如有侵权会删除。特点当调用对象方法的时候,该方法......
  • Java 集合简介 一
    什么是集合?集合就是由若干个确定的元素所构成的整体。例如,5只小兔构成的集合:在数学中,我们经常遇到集合的概念。例如:●有限集合○一个班所有的同学构成的集合;○一......
  • JavaScript学习--Array数组对象
    定义1.var变量名=newArray(元素列表);如vararr=newArray(1,2,3);2.常用:var变量名=[元素列表];如vararr=[1,2,3];访问arr[索引]=值;如arr[0]=1;ps:数组长度类型均可变 len......
  • 论文笔记 - Noisy Channel Language Model Prompting for Few-Shot Text Classificati
    Direct&&NoiseChannel进一步把语言模型推理的模式分为了:直推模式(Direct);噪声通道模式(Noisechannel)。直观来看:Direct模式NoiseChannel模式也就是说把数据......
  • JavaScript学习--基础语法03
    流程控制语句if,switch,for,while,dowhile。与之前学过的一样。 函数定义:通过function关键词定义语法:functionfunctionName(参数1,参数2) {  具体代码 }例子fu......
  • JavaScript学习--03
    运算符除了==和===其他和c语言都一样==:1.判断类型是否一样,如果不一样,则进行类型转换2.再去比较其值===:全等于1.判断类型是否一样,如果不一样,直接返回fa......