最近在面试中常常被问到IO模型,于是想在网上找找对应的讲解,可都没有一个详细好理解且带例子的。下面会介绍Java的几种IO模型,并且均给予代码演示。
Java中最常用的模型个人认为有以下四种:
- 同步阻塞IO(Blocking IO)
- 同步非阻塞IO(Non-Blocking IO)
- IO多路复用
- 异步IO(Asynchronous IO)
一、同步阻塞IO(BIO)
同步阻塞IO可以说是最简单的了,就是如果目标是不可读/写的话,那么就阻塞当前进程,直到目标可读/写。
下图以read方法举例BIO:
图中的kernel
指的是系统内核。当数据准备好时,会将数据从内核拷贝到进程中,这也就完成了读的操作。
在Linux中,网络IO默认的方式就是BIO。
下面以Java网络编程为例,写一段Server从Socket中读取Client的输入的代码:
//这是服务端类,会读取socket中的数据
public class Server {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(11522);
Socket socket = serverSocket.accept();
System.out.println("客户端已连接 at "+System.currentTimeMillis());
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
int read = ois.read(); //线程会在这里阻塞住
System.out.println("阻塞结束,读到数据:"+read+" at "+ System.currentTimeMillis());
ois.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//这是客户端类
public class Client {
public static void main(String[] args) {
try {
Socket socket = new Socket("127.0.0.1", 11522);
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
Thread.sleep(2000); //在等待两秒后才写入数据
oos.write(32);
oos.close();
socket.close();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
程序运行结果如下,从中可以明显看出,Server线程阻塞了2秒。
二、同步非阻塞IO(NIO)
同步非阻塞IO其实就是一种轮询,当数据还没有准备好的时候,不再阻塞而是直接返回一个error。然后线程再次发起请求,直到数据可读为止。然后再将数据从内核拷贝到进程中,完成读操作。
这里要说一下,Java中有个包叫java.nio
。但其实,其实现方式采用的是IO多路复用的原理,这个在下面会讲。
三、IO多路复用
个人认为多路IO复用是NIO的一种实现形式(个人认为)。IO多路复用可以说面试问得最多,也很常见。
1.Java中的IO多路复用
Java中的IO多路复用就是使用java.nio
包下的Buffer、Channel、Selector来实现的。首先说一下它们的概念:
- Buffer,顾名思义是一个缓冲区,是由一个数组实现的,存储的是在Channel中传输的数据。
- Channel,可以类比于流一样的东西,通过这个可以实现Channel双方的通信。在网络编程中,可以使用SeverSocketChannel、SocketChannel来代替SeverSocket、Socket。
- Selector,实现IO多路复用最关键的类,通过它可以获取当前注册进Selector的Channel的状态。
具体的原理和使用方法,可以详见博客:https://www.cnblogs.com/snailclimb/p/9086334.html。里面讲的非常清楚。
下面给出一段服务端的代码,其中做了较为详细的注释:
public class WebServer {
public static void main(String[] args) {
try {
ServerSocketChannel ssc = ServerSocketChannel.