IO/NIO — NIO
相比于之前讲的输入流、输出流、缓冲流等传统IO,jdk1.4提供的NIO在效率上会更高,jdk1.7时,提供了Files工具类,支持
异步Channel
的IO,效率更高区别:IO面向流,NIO面向缓冲区
一、相关概念
1、阻塞和非阻塞
- 阻塞:线程访问资源时,资源未准备好,持续等待
- 非阻塞:线程访问资源时,资源未准备好,直接相应,不会等待
2、同步和异步
- 同步:访问数据时,主动请求并等待IO操作完成
- 异步:访问数据时,请求后可以继续处理其他任务
3、IO模型
-
传统BIO模型
同步阻塞,IO在执行读写时,线程被阻塞,等待读写完成
-
伪异步IO模型
和同步阻塞类似,加入了线程池维护IO现在,效率相对BIO更高
-
NIO模型
是一种同步非阻塞IO,面向缓冲区进行操作,单个线程通过
Selector(多路复用器)
监听多个通道事件,实现高效
二、NIO的使用
1、NIO优点
- 通过注册
Channel
到Selector
上的状态来实现客户端与服务端的通信 Channel
中数据的读取是通过Buffer
,是一种非阻塞的读取方式(轮询看数据准备好没)Selector
多路复用器,单线程,线程资源开销小
2、Channel(通道)
区别于IO中读取数据时,会发生阻塞等待数据,NIO的Channel对象可以通过不同的阻塞行为,判断进行相应的操作,实现非阻塞通道
Channel`是双向的,可以读也可以写
-
Channel接口的相关实现类
FileChannel
:支持文件操作Pipe.SinkChannel
、Pipe.SourceChannel
:支持线程间通信ServerSocketChannel
:支持TCP网络通信DategramChannel
:支持UDP网络通信 -
创建方法:
根据流节点(如
InputStream
、OutputStream
)的getChannel()
方法来返回对应的 Channel(如FileChannel等) -
常用方法:
map()
:用于将 Channel 对应的部分或全部数据映射成ByteBuffer
read()
:从 Buffer 读入数据write()
:从 Buffer 写入数据
3、Buffer(缓冲区)
是一个缓冲区,也是一个容器,像一个数组,可以保存多个同类型数据在Channel读写数据时,中间需要经过Buffer
Buffer的使用
-
3个重要概念:
容量(capacity):Buffer的最大数据容量,创建后不能改变
界限(limit):位于
limit
后的区域,不能被读写位置(position):指明下一个被读或者被写入的位置索引
- 当 Buffer 装入数据结束,调用
flip()
方法,让指针复位,即上图已经读写区域清除,为输出数据做准备 - 当 Buffer 输出数据结束,调用
clear()
方法,将整个区域设为尚未读写区域
,为再次向 Buffer 中装入数据做准备(数据并未清除)
4、Selector(多路复用器)
和 Channel 相互配合使用,可以监听 Channel 的四种状态,监听到对应的状态时,才允许对 Channel进行相应的操作
-
Read:可读
-
Write:可写
-
Connect:客户端连接成功
-
Accept:准备好进行连接
5、使用示例
读取文件
public static void main(String[] args) throws IOException{
File fiel = new File("test.txt");
// 创建一个文件输入流,并获得文件输入流对应的管道
FileChannel inChannel = new FileInputStream(file).getChannel();
// 创建一个字节缓冲区(Channel的读取都要通过缓冲区完成)
ByteBuffer buf = ByteBuffer.allocate(1024);
// 将数据写入缓冲区
int readCount = inChannel.read(buf);
while((fileChannel.read(buf)) > -1){
// 装入数据完成,让指针复位,为输出数据做准备
buf.flip();
while(bug.hasRemaining()){
// 打印
System.out.print((char)buf.get())
}
// 清除缓冲区之前数据,复位标记位为0,配合 filp()使用
buf.compact();
}
}
三、jdk1.7的 NIO(改进)
提供了全面的文件IO和文件系统访问
基于异步Channel的IO
1、 Path、Paths和Files工具类
- Path:该接口代表一个与平台无关的平台路径
- Paths:获取Path对象
- Files:操作文件的工具类
2、Files工具类常用方法
-
readAllBytes(Path p):获取文件内容(byte)
该方法可以和String(byte[] b, String utf-f)一起使用,转字符串
-
write(Path p, String):将 String 写入到 p 文件中
-
copy(Path p, OutputStream out):将p文件内容复制到输出流 out 的文件中(源码里调了copy(in, out),一个输入流,一个输出流)
-
delete(Path p):将 p 文件删除
3、使用示例
获取文件内容
public static void main(String[] args){
// 文件路径
Path p = Paths.get("test.txt");
// 读取 test.txt 文件的内容,放到 byte数组中
byte[] byt = Files.readAllBytes(p);
// 将字节数组转字符串并打印
System.out.print(new String(byt, "utf8"));
}
文件复制
public static void main(String[] args){
// 文件路径
Path p = Paths.get("test.txt");
// 字符输出流
FileOutputStream out = new FileOutputStream("newTest.txt");
// 将 test.txt 复制到 newTest.txt
Files.copy(p, out);
}