前文中我们总结了linux系统中的5中IO模型,并且着重介绍了其中的4种IO模型:

  • 阻塞I/O(blocking IO)
  • 非阻塞I/O(nonblocking IO)
  • I/O多路复用(IO multiplexing)
  • 异步I/O(asynchronous IO)

  但是前面总结的IO模型只是限定在linux下,更偏向于操作系统底层的概念,并没有涉及到Java应用层面,其实Java中也提供了和前面操作系统层面的IO模型相对应的概念,这是本文接下来要讲的重点。

  同样本文会围绕如下几点进行展开:

  I/O模型在Java中的对应

  适用场景

  Java中各种IO模型的使用方式

  总结

1. I/O模型在Java中的对应

1.1 阻塞I/O

  传统Java IO提供的面向流的IO操作方式就属于阻塞式的,调用其read()或write()方法的线程会阻塞,直到完成了数据的读写,在读写的过程中线程是什么都做不了的。

1.2 非阻塞I/O

  Java NIO类库提供了多种支持非阻塞模式的类,比如Channel、Buffer,可以将其设置为非阻塞模式,线程向channel请求读数据时,只会获取已经就绪的数据,并不会阻塞以等待所有数据都准备好,这样在数据准备的阶段线程就能够去处理别的事情,这就是非阻塞式读,对于写数据是一样的。

  这里和上面阻塞的区别就是,调用read()或write()方法并不阻塞,而是会立即返回,但是这时候IO操作往往是还没有结束的。

1.3 多路复用

  Java NIO中的Selector允许单个线程监控多个channel,可以将多个channel注册到一个Selector中,然后可以"select"出已经准备好数据的channel,或者准备好写入的channel,然后对其进行读或者写数据,这就是多路复用。

1.4 异步IO

  异步IO模型是比较理想的IO模型,在异步IO模型中,当用户线程发起read操作之后,立刻就可以开始去做其它的事。另一方面,内核会等待数据准备完成,然后将数据复制到用户线程,当这一切都完成之后,内核会给用户线程发送一个信号,告诉它read操作完成了。也就是说用户线程完全不需要关心实际的整个IO操作了,只需要发起请求就行了,当收到内核的成功信号时就可以直接去使用数据了。这就是和非阻塞式的区别,如果说阻塞式IO是完全手动,非阻塞式IO就是半自动,而异步IO就是全自动,多路复用呢?我觉得可以是半自动冲锋枪^_^

  在Java 7中,提供了Asynchronous IO,Java NIO中的AsynchronousFileChannel支持异步模型实现的。

2. 适用场景

  BIO方式适用于连接数目比较小且每个连接占用大量宽带,这种方式对服务器资源要求比较高,JDK1.4以前的唯一选择,但程序直观简单易理解。

  NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,编程比较复杂,JDK1.4开始支持。

  AIO方式适用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

3. Java中各种IO模型的使用方式

  前面讲了这么多,即讲了linux下的IO模型,又讲了Java中对这些IO模型的支持,到这里我觉得是时候找一些Java中实际的例子看看,下面就分别用三种IO模型来读写文件。

3.1 通过BIO方式读写文件

  public void rwByBIO() {BufferedReader br = null;FileInputStream in = null;FileOutputStream out = null;try {in = new FileInputStream("test.txt");out = new FileOutputStream("testBIO.txt");List<Integer> list = new ArrayList();int temp;while((temp = in.read()) != -1) {out.write(temp);}br = new BufferedReader(new InputStreamReader(new FileInputStream("testBIO.txt")));System.out.println(br.readLine());}catch(Exception e) {e.printStackTrace();}finally {if(br != null) {try {br.close();}catch(IOException e) {e.printStackTrace();}}if(out != null) {try {out.close();}catch(IOException e) {e.printStackTrace();}}}}

  在根目录下准备好文件test.txt,里面写上准备好的内容,比如"黄沙百战穿金甲,不破楼兰终不还",然后跑起来,之后应该会多出一个文件testBIO.txt,里面内容是一样的。我们通过BIO的方式读取test.txt中的内容,同样以BIO的方式写入到testBIO.txt中。

3.2 通过NIO读写文件

  public void rwByNIO() {FileChannel readChannel = null;FileChannel writeChannel = null;try {readChannel = new RandomAccessFile(new File("test.txt"),"r").getChannel();writeChannel = new RandomAccessFile(new File("testNIO.txt"),"rw").getChannel();ByteBuffer buffer = ByteBuffer.allocate(10);int bytesRead = readChannel.read(buffer);while(bytesRead != -1) {buffer.flip();while(buffer.hasRemaining()) {// 写入文件writeChannel.write(buffer);}// 一次写完之后buffer.clear();bytesRead = readChannel.read(buffer);}}catch(Exception e) {e.printStackTrace();}finally {if(readChannel != null) {try {readChannel.close();} catch (IOException e) {e.printStackTrace();}}if(writeChannel != null) {try {writeChannel.close();} catch (IOException e) {e.printStackTrace();}}}}

  这里是通过NIO中的FileChannel来读写文件,但是要注意,虽然这一节的标题是说用NIO的方式来读写文件,但是FileChannel并不支持非阻塞模式,所以其实际上还是属于阻塞的,即BIO的方式,只是因为这里为了统一演示读写文件的例子,所以仍然使用NIO中的FileChannel类来完成。

3.3 通过AIO方式读写文件

    public void rwByAIO() {Path path = Paths.get("test.txt");AsynchronousFileChannel fileChannel = null;try {fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ);ByteBuffer buffer = ByteBuffer.allocate(1024);long position = 0;Future<Integer> operation = fileChannel.read(buffer, position);while(!operation.isDone());buffer.flip();Path writePath = Paths.get("testAIO.txt");if(!Files.exists(writePath)){Files.createFile(writePath);}AsynchronousFileChannel writeFileChannel = AsynchronousFileChannel.open(writePath, StandardOpenOption.WRITE);writeFileChannel.write(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() {@Overridepublic void completed(Integer result, ByteBuffer attachment) {System.out.println("bytes written: " + result);}@Overridepublic void failed(Throwable exc, ByteBuffer attachment) {System.out.println("Write failed");exc.printStackTrace();}});}catch(Exception e) {e.printStackTrace();}}

  这个例子中是通过异步地方式来读写文件。当调用了Java NIO中的AsynchronousFileChannel对这种操作提供了支持,当调用其read()方法时会立即返回一个Future对象,通过调用其isDone方法来得知数据是否读取完毕。

4. 总结

  本文结合前文讲到的IO模型,分别对应到Java中的具体类库实现,并通过例子演示了BIO、NIO、AIO三种方式读写文件。

  • 标准Java IO提供的面向流的方式属于BIO模型的实现,在读取的过程中是会阻塞的;
  • Java NIO提供的Channel和Buffer是支持NIO模式的,调用了Channel的读写方法之后可以立即返回,在往Buffer中准备数据的过程中是不阻塞的,线程可以做别的事情,但是从Buffer读写数据是阻塞的;
  • Java NIO中提供的AsynchronousFileChannel支持异步读写文件,当调用了其读写方法之后可以立即返回,只需要等待系统把数据复制到指定位置即可,整个过程都不会阻塞;

参考文献

bio-vs-nio-vs-aio

linux-io

Java NIO学习系列六:Java中的IO模型相关推荐

  1. Java NIO学习系列四:NIO和IO对比

    前面的一些文章中我总结了一些Java IO和NIO相关的主要知识点,也是管中窥豹,IO类库已经功能很强大了,但是Java 为什么又要引入NIO,这是我一直不是很清楚的?前面也只是简单提及了一下:因为性 ...

  2. Java NIO学习系列七:Path、Files、AsynchronousFileChannel

    相对于标准Java IO中通过File来指向文件和目录,Java NIO中提供了更丰富的类来支持对文件和目录的操作,不仅仅支持更多操作,还支持诸如异步读写等特性,本文我们就来学习一些Java NIO提 ...

  3. Java NIO学习系列五:I/O模型

    前面总结了很多IO.NIO相关的基础知识点,还总结了IO和NIO之间的区别及各自适用场景,本文会从另一个视角来学习一下IO,即IO模型.什么是IO模型?对于不同人.在不同场景下给出的答案是不同的,所以 ...

  4. Java NIO学习系列三:Selector

    前面的两篇文章中总结了Java NIO中的两大基础组件Buffer和Channel的相关知识点,在NIO中都是通过Channel和Buffer的协作来读写数据的,在这个基础上通过selector来协调 ...

  5. Java NIO学习系列二:Channel

    上文总结了Java NIO中的Buffer相关知识点,本文中我们来总结一下它的好兄弟:Channel.上文有说到,Java NIO中的Buffer一般和Channel配对使用,NIO中的所有IO都起始 ...

  6. Java NIO学习系列一:Buffer

    从本文开始我会开始总结NIO部分,Java NIO(注意,这里的NIO其实叫New IO)是用来替换标准Java IO以及Java 网络API的,其提供了一系列不同与标准IO API的方式来处理IO, ...

  7. [初级]Java命令学习系列(六)——jinfo

    转载自 [初级]Java命令学习系列(六)--jinfo jinfo可以输出java进程.core文件或远程debug服务器的配置信息.这些配置信息包括JAVA系统参数及命令行参数,如果进程运行在64 ...

  8. Java NIO 学习笔记(三)----Selector

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  9. Java nio 学习笔记 相关知识

    http://blog.csdn.net/tsyj810883979/article/details/6876594 一.基本概念 IO 是主存和外部设备 ( 硬盘.终端和网络等 ) 拷贝数据的过程. ...

最新文章

  1. R语言ggplot2可视化:通过水平半小提琴图(Horizontal Half Violin Plots)和抖动数据点( jittered data)可视化雨云图(Rain Cloud plots)
  2. 使用RNN神经网络自动生成名字 (不使用深度学习框架,源码)
  3. OpenStack Neutron浅析(一)
  4. 【Android 应用开发】Canvas 绘制文字 ( 文字尺寸测量 | 基线绘制 )
  5. [算法学习]斐波那契数的计算
  6. 大力出奇迹!6144块TPU,5400亿参数,会改bug、解读笑话,谷歌刚刚用Pathways训练了一个大模型...
  7. DevExpress第三方控件汉化的全部代码和使用方法
  8. 获取工作流活动的返回值
  9. mysql 恢复root用户_mysql误删root用户恢复方案
  10. Jenkins构建Spring+Nodejs项目
  11. MySQL------如何将SQLServer文件数据迁移到MySQL
  12. 嵌入式Linux系统编程学习之三十一线程的属性
  13. 魅族16T“巨型”包装盒引人注目:旁边的16s Pro不要面子啊
  14. MATLAB建立图表的基础
  15. 3升5升得4升——倒水问题的万能解法(扩展欧几里得算法)
  16. 漫谈工业软件(4)-关于开源工业控制软件
  17. 金晨想在无人车上劈叉,撒贝宁与数字祝融号对话…这届百度世界大会,有被惊艳到...
  18. 进程间通信IPC(一)pipe fifo mmap
  19. UWB简介及其定位方法
  20. 程序员们,你会考虑使用中文编程吗?

热门文章

  1. Kafka设计解析(五):Kafka Benchmark--转
  2. Linux ps aux指令詳解--转
  3. 基于 Quartz 开发企业级任务调度应用--转
  4. jquery学习手记(8)遍历
  5. 菜菜sklearn——XGBoost(2)
  6. 李彦宏要给百度全员涨薪 连实习生都雨露均沾 :再没有行动,人都要被字节跳动给挖光了。
  7. 2018年区块链技术发展总结与展望(附报告全文) | 起风研究院
  8. Starzhou:EOSIO1.0 版本环境搭建
  9. 解密个性化资讯推荐技术
  10. Ben Horowitz:执行程序有多糟糕,公司倒闭就有多快