BIO使用同步阻塞的方式工作,而NIO则使用的是异步阻塞的方式。对于NIO而言,它最重要的地方是当一个链接被创建后,不需要对应一个线程,这个连接会被注册到多路复用器上面,所以所有的连接只需要一个线程管理,当这个线程中的多路复用器进行轮询的时候,发现连接上游请求的话,才开启一个线程进行处理,也就是一个请求一个线程模式。
在NIO的处理方式中,当一个请求来的话,开启线程进行处理,但是它仍然需要使用阻塞的方式读取数据,显然在这种情况下这个线程就被阻塞了,在大并发环境下也会有一定的性能问题。造成这个问题的主要原因是NIO仍然使用了同步的IO。
AIO是对NIO的改进,所以AIO又叫做NIO.2,它是基于Proactor模型实现的。
在IO读写的时候,如果想把IO请求与读写操作分离调配进行,那么就需要用到事件分离器。根据处理机制的不同,事件分离器又分为:同步的Reactor和异步的Proactor。为了更好的理解AIO与NIO的区别,下面首先简要介绍一下Reactor模型与Proactor模型的区别。
1,Reactor模型
它的工作原理以读操作为例:
1) 应用程序在事件分离器上注册“读就绪事件”与“读就绪事件处理器”
2) 时间分离器会等待读就绪事件发生。
3) 一旦读就绪事件发生事件分离器就会被激活,分离器就会调用“读就绪事件处理器”
4) 此时读就绪处理器就知道有数据可以读了,然后开始读取数据,把读到的数据提交程序使用。
2,Proactor模型
它的工作原理以读操作为例:
1) 应用程序在事件分离器上注册“读完成事件”和“读完成事件处理器”,并向操作系统发出异步读请求。
2) 事件分离器会等待操作系统完成读取。
3) 在操作系统完成数据的读取并将结果数据存入用户自定义缓冲区后会通知事件分离器读操作完成。
4) 事件分离器监听到“读完成事件”后会激活“读完成事件处理器”
5) 读完成事件处理器此时就可以把读取到的数据提供给应用程序使用。
3,两模型主要区别
在Reactor模型中,应用程序需要负责数据的读取操作;而在Proactor模型中,应用程序只需以缓存读取和写入,实际的IO由操作系统负责,由此可以看出AIO的处理流程如下:
1) 每个socket连接在事件分离器注册“IO完成事件”和“IO完成事件处理器”
2) 应用程序需要进行IO操作时,会向分离器发出IO请求并把所需的Buffer区域高速分离器,分离器则会通知操作系统进行IO操作。
3) 操作系统则尝试IO操作,等操作系统完成后会通知分离器。
4) 分离器检测到IO完成事件,则激活IO完成事件处理器,处理器会通知应用程序,接着应用程序就可以直接从Buffer区进行数据的读写。
在AIO socket编程中,服务端通道是AsynchronousServerSocketChannel,这个类提供了一个open静态工厂,一个bind方法用于绑定服务器端ip地址,还有端口号,另外还提供了accept用于接收用户的连接请求。在客户端使用的通道是AsynchronousSocketChannel,这个通道处理提供open静态工厂方法外,还提供了read和write方法。
在AIO编程中,当应用程序发出一个事件accept、read、write等后需要指定事件处理类,也就是回调函数,AIO中使用的事件处理类是CopletionHandler<V,A>,这个接口有如下两个方法,分别在异步操作成功和失败时被回调。
1 public class Server {
2
3 private void listen(int port) {
4 try {
5 try (AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open()) {
6 server.bind(new InetSocketAddress(port));
7 System.out.println("Server is listening on " + port);
8 ByteBuffer buff = ByteBuffer.allocateDirect(5);
9 server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
10 //accetp成功后调用这个方法
11 @Override
12 public void completed(AsynchronousSocketChannel result, Object attachment) {
13 try {
14 buff.clear();
15 result.read(buff).get();
16 buff.flip();
17 //回显客户端端发送的数据
18 result.write(buff);
19 buff.flip();
20 } catch (InterruptedException | ExecutionException e) {
21 e.printStackTrace();
22 } finally {
23 try {
24 result.close();
25 server.close();
26 } catch (Exception e) {
27 e.printStackTrace();
28 }
29 }
30 }
31 @Override
32 public void failed(Throwable exc, Object attachment) {
33 exc.printStackTrace();
34 }
35 });
36 //一直等待
37 try {
38 Thread.sleep(Integer.MAX_VALUE);
39 } catch (InterruptedException e) {
40 e.printStackTrace();
41 }
42 }
43 } catch (Exception e) {
44 e.printStackTrace();
45 }
46 }
47
48 public static void main(String args[]) {
49 new Server().listen(8080);
50 }
51 }
1 public class Client {
2
3 private final AsynchronousSocketChannel client;
4
5 public Client() throws Exception {
6 client = AsynchronousSocketChannel.open();
7 }
8
9 public void start() throws Exception {
10 client.connect(new InetSocketAddress("127.0.0.1", 8080), null, new CompletionHandler<Void, Object>() {
11 @Override
12 public void completed(Void result, Object attachment) {
13 try {
14 client.write(ByteBuffer.wrap("Hello".getBytes())).get();
15 } catch (Exception e) {
16 e.printStackTrace();
17 }
18 }
19 @Override
20 public void failed(Throwable exc, Object attachment) {
21 exc.printStackTrace();
22 }
23 });
24 final ByteBuffer bb = ByteBuffer.allocate(5);
25 //数据读取完成调用的方法
26 client.read(bb, null, new CompletionHandler<Integer, Object>() {
27 @Override
28 public void completed(Integer result, Object attachment) {
29 System.out.println(result);
30 System.out.println(new String(bb.array()));
31 }
32 @Override
33 public void failed(Throwable exc, Object attachment) {
34 exc.printStackTrace();
35 }
36 });
37 //一直等待
38 try {
39 Thread.sleep(Integer.MAX_VALUE);
40 } catch (InterruptedException e) {
41 e.printStackTrace();
42 }
43 }
44
45 public static void main(String args[]) throws Exception {
46 new Client().start();
47 }
48 }