一、基本IO概念
1、什么是流IO?
1.1 基本概念
流是一种抽象概念,它代表了数据的无 结构化传递。 按照流的方式进行输入输出,数据被当成无结构的字节序或字符序列。从流中取得数据的操作称为提取操作,而 向流中添加数据的操作称为插入操作。用来进行输入输出操作的流就称为IO流。换句话说,IO流就是以流的方式进行输入输出。输入输出(IO)是指计算机同 任何外部设备之间的数据传递。常见的输入输出设备有文件、键盘、打印机、屏幕等。数据可以按记录(或称数据块)的方式传递,也可以流的方式传递。
1.2 形象比喻
流(stream)的概念源于UNIX中管道(pipe)的概念。在UNIX中,管道是一条不间断的字节流,用来实现程序或进程间的通信,或读写外围设备、外部文件等。一 个流,必有源端和目的端,它们可以是计算机内存的某些区域,也可以是磁盘文件,甚至可以是Internet上的某个URL。流的方向是重要的,根据流的方 向,流可分为两类:输入流和输出流。用户可以从输入流中读取信息,但不能写它。相反,对输出流,只能往输入流写,而不能读它。
实际上,流的源端和目的端可简单地看成是字节的生产者和消费者,对输入流,可不必关心它的源端是什么,只要简单地从流中读数据,而对输出流,也可不知道它的目的端,只是简单地往流中写数据。形象的比喻——水流 ,文件======程序 ,文件和程序之间连接一个管道,水流就在之间形成了,自然也就出现了方向:可以流进,也可以流出。便于理解,这么定义流: 流就是一个管道里面有流水,这个管道连接了文件和程序。
2. 字节流和字符流的区别
字节流与字符流主要的区别是他们的的处理方式。
字节流是最基本的,所有的InputStream和OutputStream的子类都是,主要用在处理二进制数据,它是按字节来处理的。但实际中很多的数据是文本,又提出了字符流的概念,它是按虚拟机的encode来处理,也就是要进行字符集的转化这两个之间通过 InputStreamReader,OutputStreamWriter()来关联,实际上是通过byte[]和String来关联。在实际开发中出现的汉字问题实际上都是在字符流和字节流之间转化不统一而造成的。
1 public String(byte bytes[], String charsetName)
在从字节流转化为字符流时,实际上就是byte[]转化为String时,有一个关键的参数字符集编码,通常我们都省略了,那系统就用操作系统的 lang。而在字符流转化为字节流时,实际上是String转化为byte[]时,byte[] String.getBytes(String charsetName),也是一样的道理。至于java.io中还出现了许多其他的流,按主要是为了提高性能和使用方便,如 BufferedInputStream,PipedInputStream等。
注:InputStreamReader源码
//是字节流转换成字符流,按照某种编码的方式 public InputStreamReader(InputStream in) { super(in); try { //这里的StreamDecoder源码看不到 sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object } catch (UnsupportedEncodingException e) { // The default encoding should always be available throw new Error(e); } }
个人理解:
1、单就IO流来讲,“流”传输的东西始终是无结构的一个个字节序列,字节流和字符流的区别在于,它们是基于不同的“协议”处理这些字节:
2、字节流----按照面向字节处理数据,支持8位结构的字节,不能很好的处理16位Unicode字符。只能用于处理图像、音频等文件;
字符流----按照面向字符处理数据,能够较好的处理16位的Unicode字符(比如char类型),更好的处理字符类型。(提高效率)
3、处理数据源的时候,尽量使用字符流。另外IO流中大量使用的装饰器模式:这也是处理单一流对象,却要创建这么多对象。提高工作效率。
2、什么是块IO?
2.1 基本概念
所谓记录,是指有着内部结构的数据块。记录内部除了有需要处理的实际数据之外,还可能包含附加信息,这些附加信息通常是对本记录数据的描述。
3、Java流IO和块IO有什么区别?
操作系统并非不能快速传送数据,让 Java 有事可做;相反,是 JVM 自身在 I/O 方面效率欠佳。 操作系统与 Java 基于流的 I/O模型有些不匹配。 操作系统要移动的是大块数据(缓冲区),这往往是在硬件直接存储器存取(DMA)的协助下完成的。而 JVM 的 I/O 类喜欢操作小块数据——单个字节、几行文本。结果,操作系统送来整缓冲区的数据,java.io 的流数据类再花大量时间把它们拆成小块,往往拷贝一个小块就要往返于几层对象。操作系统喜欢整卡车地运来数据,java.io 类则喜欢一铲子一铲子地加工数据。有了 NIO,就可以轻松地把一卡车数据备份到您能直接使用的地方(ByteBuffer 对象)。
1)面向流I/O的系统:一
次处理一个字节的数据。一个输入流每次会读入一个字节的数据,一个输出流同样每次次消费一个字节的数据。对于流式数据,很容易创建过滤器。可以相对简单地
把几个过滤器连接在一起,每个过滤器完成自己的工作,也是按字节进行过滤,精细的处理机制。另一方面,面向流I/O的通信往往比较缓慢。
2)面向块I/O的系统:以块为单位处理数据。每个操作步骤会生成或消费一个块的数据。以块为单位处理数据,其处理速度远快于以字节流为单位的方式。但是,与面向流I/O的通信相比,面向块I/O的通信缺乏优雅和简洁。 但是有更高的数据吞吐量(指对网络、设备、端口、虚电路或其他设施,单位时间内成功地传送数据的数量)。
个人理解:
NIO:NIO模型,不再使用原来的IO模型(往流中读写数据),而是要从缓存区往通道读写数据。那有人又要问,我IO流对象经过BufferInputStream包装一下,不也可以按“缓存区”读吗?
但是,NIO缓冲区的底层实现将缓冲区ByteBuffer直接与硬件或者内存连接;而IO是操作系统内存将数据复制到JVM内存中,再在JVM内存上进行操作。
4、流和通道的区别:从编程角度来讲,流是基于字节的,而通道是基于块的。
1、从基本概念上看,流设计为一次传送一个字节的数据。而通道会传送缓冲区中的数据块。可以读写通道的字节之前,这些字节必须存在缓存区中,而且一次性会读写一个缓存区的数据。
2、通道和缓冲区支持对同一个对象的读写,程序可以读写同一个通道;而流,分为读/写两种。
3、除了数据列表以外,每个缓冲区还记录了信息的4个关键部分:
位置(pos) 缓冲区中将读取或写入的下一个位置。
容量(cap) 缓冲区可以保存的元素的最大数目。
限度(lim) 缓存区可以访问数据的末尾位置。
标记(mark)缓冲区客户端指引的索引。
4、与读取InputStream不同,读取缓冲区实际上不会以任何方式改变缓冲区中的数据。只可能向前或向后设置位置,从而可以从缓冲区中某个特定位置开始读取(灵活)。而IO流只能从某个位置开始一个个去读写(固定)。Buffer在实现上本质是一个数组,其作用是一个存储器,或者分段运输区,并且提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程。
二、同步/异步 阻塞/非阻塞
(透彻版:http://www.zhihu.com/question/19732473)
(详细版:http://www.cnblogs.com/zhuYears/archive/2012/09/28/2690194.html)
看完这两篇博文的个人理解:
1、同步与异步(消息通知机制):
同步:线程去调用资源的时候,如果资源没有准备好,那么线程会一直等待,当资源准备好了,那么该线程就调用该资源,并返回资源;
异步:线程只负责发出一个调用通知,不管资源准没准备好,我都立刻返回。等资源准备好,会自己借助别的方式返回。(在Linux中会使用内核返回、回调)
2、阻塞与非阻塞:该线程的等待调用结果的状态:
阻塞:如果去调某个资源的时候,没有准备好,那么线程被挂起,等到资源准备好,返回可以调用的标识,线程再被“启用”。
非阻塞:如果去调某个资源的时候,如果该资源已经准备好,那么线程就可以直接调取该资源;如果资源没有准备好,那么该线程就处理别的事情,时不时过来看看资源有没有准备好(资源返回的标志位)
ps:把自己平时容易混淆的东西记录下来,想到什么就加上了,所以有些乱。。。后面会正式步入JavaNIO
参考博客:
http://www.cnblogs.com/pepcod/archive/2013/01/20/2913435.html
百度百科
http://my.oschina.net/OutOfMemory/blog/95853
《Java网络编程 第四版》