BIO编程
使用统一接口进行操作,具体实现细节无关
字节流
InputStream和OutputStream,都实现了Closeable接口,所以支持try-resources
InputStream操作用于实现数据的读取操作
-
read():int 注意这里只是读取一个字节,0-255之间,-1表示流结束
-
read(byte[]):int 返回值表示读取的具体字节个数,-1流结束
-
close():void 关闭流
-
另外不重要的方法 read(byte[],int,int) available() skip(long)
OutputStream操作方法用于实现数据的写出操作
-
write(int):void 写出一个字节,int的低8位
-
write(byte[]具体数据,int起始下标,int长度):void
-
close():void 关闭流
-
不重要的方法write(byte[])、flush()
字符流
顶级父抽象类Reader和Writer,一次一字符的操作,实现了Closeable接口。如果涉及中文信息,则需
要考虑编码字符集的问题,如果编码字符集错误,则显示乱码
Reader用于封装字符读取操作
-
read():int 返回读取到的字符数据,0-65535 2B,返回-1 表示流末尾
-
read(char[]):int 返回读取的字符个数,流结束返回-1
-
close():void 关闭流
Writer用于封装字符写出操作
-
write(int):void 写出低16位
-
write(char[]数据,int起始下标,int写出的字符数):void
-
close():void 关闭流,释放资源
-
write(String):void 写出一个字符串内容到输出流中
节点流
类型 | 字符流 | 字节流 |
---|---|---|
文件 | FileReader和FileWriter | FileInputStream和FileOutputStream |
数组 | CharArrayReader和CharArrayWriter | ByteArrayInputStream和ByteArrayOutputStream |
字符串 | StringReader和StringWriter | |
线程通讯使用的管道 | PipedReader和PipeWriter | PipedInputStream和PipeOutputStream |
文件流
FileInputStream和FileReader用于从一个文件中读取数据;FileOutputStream和FileWriter用于向一个
文件中写出数据
FileInputStream文件输入字节流,FileReader文件输入字符流,用法类似
-
FileInputStream(“文件名称”),如果文件不存在则FileNotFoundException
-
FileInputStream(File)
FileOutputStreram文件输出字节流,FileWriter文件输出字符流,用法类似
-
FileOutputStream(“文件名称”);如果文件不存在,则自动创建;如果文件存在则执行覆盖原文件内容
-
FileOutputStream(“文件名称”,boolean是否采用追加方式);如果文件不存在,则自动创建;如果文件存在并且Boolean参数为true则表示采用追加的方式处理
NIO
NIO是采用内存映射文件的方式处理输出输出,可以将文件或者文件的一段区域映射到内存中,然后就
可以像访问内存一样来访问文件,所以进行输入输出的速度比BIO快的多
-
BIO是同步阻塞式;NIO是同步非阻塞式
-
BIO是面向流的,NIO是面向缓冲区的
-
NIO是一个线程从某个通道发送读取请求时,仅仅获取当前可以用的数据不会保持阻塞等待
NIO基础
NIO中由3个核心对象Channel通道、Buffer缓冲和Selector多路复用器
-
Channel是对传统输入输出系统的模拟,可以通过map方法将一块数据映射到内存中
-
Buffer本质上就是一个数组,发送到Channel中的所有数据都先放入到buffer中,针对基本数据类型提供7种不同的buffer,没有boolean类型没有
-
selector多路复用器,针对事件进行选择,实现一对多的连接效果
Buffer抽象类
Buffer主要作用就是用于转入数据,然后输出数据。最基本的实现是ByteBuffer,可以在字节数组上进行 get/set操作;另外针对基本数据类型,系统提供了7种具体的实现,例如CharBuffer、ShortBuffer、DoubleBuffer
- 静态方法allocate(int capacity)创建一个容积为capacity的xxxBuffer对象
buffer有3个核心概念:容积capacity、界限limit和位置position
-
capacity表示该buffer的最大数据容量,一旦创建对象则不能修改
-
limit表示位于limit之后的数据既不可读,也可写
-
position表示下一次可读写的索引位置
-
mark标记特定位置,需要注意只能在0到position之间
常用方法
-
capacity():int 返回buffer的容积值
-
hasRemaining():boolean 判断是否还有数据可以进行处理
-
remaing():int 获取position当前位置和limit界限之间的元素个数
-
position():int 返回当前的操作位置
-
mark():Buffer 设置标记位置,已被后面重返这个位置
-
reset():Buffer 将位置position重新返回到mark所在为位置
-
rewind():Buffer 将position重新设置到0,并取消mark标记
-
put(obj) :xxxBuffer 用于向buffer中添加数据
-
get():xxx 用于从buffer中获取数据
Channel通道
Channel可以直接将文件的部分或者全部映射到 buffer中。注意不能直接访问Channel中的数据,包括读写都不行,Channel只能与buffer进行交互
- 所有的Channel不应该使用构造器来直接创建,而是通过传统的InputStream\OutputStream的getChannel方法获取。一般使用FileInputStream和FileOutputStream中的 getChannel获取
selector
是Java NIO核心组件中的一个,用于检查一个或多个NIO Channel通道的状态是否处于可读、可写。如此可以实现单线程管理多个channels也就是可以管理多个网络链接。
使用Selector的好处在于: 使用更少的线程来就可以来处理通道了, 相比使用多个线程,避免了线程上下文切换带来的开销
-
FileChannel不能切换为非阻塞模式,更准确的来说是因为FileChannel没有继承SelectableChannel
-
多用于网络应用编程中
基本用法
-
Selector的创建。通过调用Selector.open()方法创建一个Selector对象
Selector selector = Selector.open();
-
注册Channel到Selector
channel.configureBlocking(false); SelectionKey key = channel.register(selector, Selectionkey.OP_READ);
Channel必须是非阻塞的。
-
轮询方式获取选择器上的状态值
while(selector.select()>0){ Iterator<SelectionKey> it=selector.selectedKeys().iterator(); ... }
Charset字符集
所有的文件底层都是二进制数据存储的,字符文件是系统将底层的二进制序列转换为字符,这里一定会涉及编码字符集和对应的编码器和解码器
-
将明文的字符序列转换为计算机理解的二进制序列称为编码
-
将二进制序列转换为明文字符串称为解码
Charset类
Charset.availableCharsets()获取当前JDK所支持的所有字符集
//获取当前JDK所支持的所有字符集
SortedMap<String, Charset> sm = Charset.availableCharsets();
for(String tmp:sm.keySet()) {
System.out.println(tmp);
}
字符串别名:
-
GBK简体中文 GB2312
-
ISO-8859-1 拉丁文 欧洲的所有编码
-
UTF-8 变长编码,支持中文,实际上是Unicode编码的一种实现
构建对象
Charset c=Charset.forName("GBK");
文件锁FileLock
文件锁用于阻止多个进程并发修改同一个文件
在NIO中提供了FileLock支持文件锁定功能
-
在FileChannel中提供了lock/tryLock方法可以获取文件锁对象以实现锁定文件,避免同时修改
- lock()用于锁定某个文件,如果无法获取文件锁,则程序一直阻塞等待
- tryLock()是尝试锁定文件,如果可以锁定则直接返回该文件锁,如果不能锁定也是立即返回null,不会出现阻塞等待
-
锁可以是排它锁或者共享锁,shared=true表示共享,允许多个进程同时读取文件,但是会阻止其它进程获取该文件的排他锁
文件锁是建议性质,不是强制性。
AIO
异步非阻塞式
JDK7新增了一些和文件、网络IO相关的API,AIO最大的特性就是异步处理能力,一般用于网络编程和大文件IO处理中。AIO其实就是一种在读写操作结束之前允许执行其它操作的IO处理,等读写执行完毕自动通知调用后续处理
提供了3个异步处理通道
-
AsynchronousFileChannel用于文件的异步读写
-
AsynchronousSocketChannel用于socket客户端异步读写
-
AsynchronousServerSocketChannel用于serverSocket服务端异步读写
异步无非是通知系统做一件事情,然后自己做其它事情,异步调用如何进行后续处理,处理方式有2种:
-
将来式,主线程发起异步请求,轮询并等待结果使用
-
回调式,异步回调
AIO的具体实施需要充分调用操作系统OS参与,IO需要OS支持,并发也同样需要OS支持,所以OS不同时执行执行性能方便差异会比较明显,因此在具体的应用种AIO使用并不是很广泛
BIO vs NIO vs AIO
概念
BIO:同步阻塞,实现模式为一个连接一个线程,就是客户端连接请求时,服务器需要启动一个线程进行处理
NIO:同步非阻塞,实现模式为一个连接一个线程,客户端发起请求时会注册到selector多路复用器上,多路复用器会轮询所有有IO请求时才启动一个线程进行处理
AIO:异步非阻塞,一个有效请求一个线程,客户端IO请求都是由OS操作系统先完成了再通知服务器应用启动线程进行处理
场景
-
BIO适用于连接数目比较小且固定的架构,这种方式对服务器资源要求较高,并发局限于应用中,JDK1.4以前的唯一选择,但是程序直观、简单且易于理解
-
NIO适用于连接数目比较多且连接时间较短的架构中,例如聊天服务器,并发局限于当前应用中,编程相对比较复杂,从JDK1.4开始支持
-
AIO适用于连接数目多且连接时间长的架构中,例如相册服务器,可以充分调用OS参与同步IO处理,而且proactor用于异步IO中
区别
-
同步阻塞:用户进程发起一个IO操作请求后,必须等待IO操作完成后,用户进程才能继续运行后续操作,否则用户进程阻塞等待
-
同步非阻塞:用户进程发起一个IO操作请求后可以返回做其它事情,但是用户进程需要不时询问IO操作是否已经就绪,这里会造成不必要的CPU资源浪费
-
异步非阻塞:用户进程发起一个IO操作请求后可以立即返回做其它事情,等到IO操作真正完成后,应用程序会收到IO操作完成的通知,此时用户进程只需要对数据进行处理即可