BIO/NIO/AIO知识点总结
- 1. Java IO和流
- 1.1 流的分类
- 2. Java IO
- 2.1 InputStream和OutputStream用例
- 2.2 Writer和Reader用例
- 3. IO的同步/异步和阻塞/非阻塞
- 4. BIO,NIO,AIO
- 参考材料
1. Java IO和流
IO的全程是Input/Output,也就是输入输出。
在Java当中,数据的处理都是以流(Stream)的方式来进行,流在这里是一个抽象概念,指代所有可以产出数据的对象或者是数据源(像是键盘,文件,网络等都是流)。通过流的形式Java可以简化数据源的模型来进行数据处理。
1.1 流的分类
按来源区分:
- 输入流:程序打开一个数据源,按顺序从数据源中读取数据到程序中。
- 输出流:程序打开一个目的源,按顺序把数据写到目的源中。
按数据单元区分:
- 字节流:读取的单位是字节,也就是每次只读取1byte长度的数据。
- 字符流:读取的单位是字符,在Java中1字符的长度是2字节。
按流的角色区分:
- 节点流:有连接到实际的数据源的流。
- 处理流:对已有的流进行连接和封装,再进行数据处理的流。也被称作高级流。
2. Java IO
IO的类基本都在Java.io包下面,基本按数据单元分为两个大类
- 字节流:用InputStream和OutputStream
- 字符流:Reader和Writer
2.1 InputStream和OutputStream用例
首先,这两个类是抽象类,不能直接使用,只能通过它的子类来使用。这里就用FileInputStream和FileOutputStream。
public class StreamDemo {
public static void main(String[] args) {
String path = "test.txt";
createFile(path);
output(path);
input(path);
}
// 读取文件的内容
public static void input(String path){
FileInputStream inputStream = null;
byte[] content = new byte[1024];
try{
// 实例化文件输入流
inputStream = new FileInputStream(path);
// 读取文件,由于字节流的单位长度是byte,所以用byte数组来存储
int len = inputStream.read(content);
// 输出数据。不能直接转,会有无效字符,要填上区间。
System.out.println(new String(content, 0, len));
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 往文件写入内容
public static void output(String path){
String content = "hello Java IO";
FileOutputStream outputStream = null;
try{
// 实例化文件输出流
outputStream = new FileOutputStream(path);
// 写内容,由于字节流的单位长度是Byte,所以要转成Byte。
outputStream.write(content.getBytes());
outputStream.close();
}catch (IOException e) {
e.printStackTrace();
}finally {
}
}
public static void createFile(String path){
File file = new File(path);
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2.2 Writer和Reader用例
Writer和Reader也是抽象类,要用它的子类实例化,这里以FileWriter和FileReader为例。
public class WriterDemo {
public static void main(String[] args) {
String path = "test2.txt";
createFile(path);
write(path);
read(path);
}
public static void write(String path){
String content = "hello Java IO";
FileWriter fileWriter = null;
try {
// 实例化流
fileWriter = new FileWriter(path);
// 对于字符流而言,输出用char,源码会将String转成char。
fileWriter.write(content);
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void read(String path){
FileReader fileReader = null;
char[] content = new char[1024];
try {
// 实例化流
fileReader = new FileReader(path);
// 对于字符流而言,用char数组直接接收
int len = fileReader.read(content);
System.out.println(new String(content, 0, len));
fileReader.close();
}catch (IOException e){
e.printStackTrace();
}
}
public static void createFile(String path){
File file = new File(path);
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3. IO的同步/异步和阻塞/非阻塞
同步异步是被调用一方的概念,具体体现在被调用一方的通知方式上。
- 同步:A调用了B,然后B在完成任务(或失败任务)后通过原有的调用通知A。
- 异步:A调用了B,然后B在完成任务(或失败任务)后通过状态,监听等别的方式通知A。
阻塞和非阻塞是调用一方的概念,具体体现在调用一方在等待结果的过程中是否继续工作。
- 阻塞:A调用了B,在B完成任务的时间内,A啥事都不干就在这白等,直到B完成任务通知A后A才继续工作。
- 非阻塞:A调用了B,在B完成任务的时间内,A直接去做别的事。
阻塞非阻塞和同步异步两两组合有四种结果:
组合方式 | 性能分析 |
同步阻塞(BIO) | 最常用的一种用法,使用也是最简单的,但是 I/O 性能一般很差,CPU 大部分在空闲状态。 |
同步非阻塞(NIO) | 提升 I/O 性能的常用手段,就是将 I/O 的阻塞改成非阻塞方式,尤其在网络 I/O 是长连接,同时传输数据也不是很多的情况下,提升性能非常有效。 这种方式通常能提升 I/O 性能,但是会增加CPU 消耗,要考虑增加的 I/O 性能能不能补偿 CPU 的消耗,也就是系统的瓶颈是在 I/O 还是在 CPU 上。 |
异步阻塞 | 这种方式在分布式数据库中经常用到,例如在网一个分布式数据库中写一条记录,通常会有一份是同步阻塞的记录,而还有两至三份是备份记录会写到其它机器上,这些备份记录通常都是采用异步阻塞的方式写 I/O。异步阻塞对网络 I/O 能够提升效率,尤其像上面这种同时写多份相同数据的情况。 |
异步非阻塞(AIO) | 这种组合方式用起来比较复杂,只有在一些非常复杂的分布式情况下使用,像集群之间的消息同步机制一般用这种 I/O 组合方式。如 Cassandra 的 Gossip 通信机制就是采用异步非阻塞的方式。它适合同时要传多份相同的数据到集群中不同的机器,同时数据的传输量虽然不大,但是却非常频繁。这种网络 I/O 用这个方式性能能达到最高。 |
4. BIO,NIO,AIO
- BIO:Blocking IO,即同步阻塞IO。我们平时用到的InputStream和OutputStream还有Reader、Writer就是BIO。
- NIO:Non-blocking IO或者New IO,即同步非阻塞IO。由于BIO的效率不高,JDk1.4引入了java.nio包,里面有Nio相关的类,性能会比BIO好。
- AIO:Asynchronous IO,JDK1.7后加入的异步非阻塞IO。
参考材料
从同步阻塞聊到Java三种IO方式 | Daryl’s Blog