Java语言进阶:NIO2-AIO(异步、非阻塞)
AIO概述
在 Java 7 中,NIO 有了进一步的改进,也就是 NIO 2,引入了异步非阻塞 IO 方式,也有很多人叫它 AIO(Asynchronous IO)。
同步,异步,阻塞,非阻塞概念回顾
同步:调用方法之后,必须要得到一个返回值。
异步:调用方法之后,没有返回值,但是会有回调函数。回调函数指的是满足条件之后会自动执行的方法
阻塞:如果没有达到方法的目的,就一直停在这里【等待】。
非阻塞:不管有没有达到目的,都直接【往下执行】。
AIO相关类和方法介绍
AIO是异步IO的缩写,虽然NIO在网络操作中,提供了非阻塞的方法,但是NIO的IO行为还是同步的。对于NIO来说,我们的业务线程是在IO操作准备好时,得到通知,接着就由这个线程自行进行IO操作,IO操作本身是同步的。
但是对AIO来说,则更加进了一步,它不是在IO准备好时再通知线程,而是在IO操作已经完成后,再给线程发出通知。因此AIO是不会阻塞的,此时我们的业务逻辑将变成一个回调函数,等待IO操作完成后,由系统自动触发。
与NIO不同,当进行读写操作时,只须直接调用API的read或write方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。 即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。 在JDK1.7中,这部分内容被称作NIO.2---->AIO,主要在Java.nio.channels包下增加了下面四个异步通道:
- AsynchronousSocketChannel
- AsynchronousServerSocketChannel
- AsynchronousFileChannel
- AsynchronousDatagramChannel
在AIO socket编程中,服务端通道是AsynchronousServerSocketChannel,这个类提供了一个open()静态工厂,一个bind()方法用于绑定服务端IP地址(还有端口号),另外还提供了accept()用于接收用户连接请求。在客户端使用的通道是AsynchronousSocketChannel,这个通道处理提供open静态工厂方法外,还提供了read和write方法。
在AIO编程中,发出一个事件(accept read write等)之后要指定事件处理类(回调函数),AIO中的事件处理类是CompletionHandler<V,A>,这个接口定义了如下两个方法,分别在异步操作成功和失败时被回调。
void completed(V result, A attachment);
void failed(Throwable exc, A attachment);
实操–AIO 同步连接同步读
需求
- AIO同步写法,读取客户端写过来的数据
分析
- 获取AsynchronousServerSocketChannel对象,绑定端口
- 同步接收客户端请求
- 读取数据
实现
public static void main(String[] args) throws Exception {
//创建对象
AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open();
//绑定端口
assc.bind(new InetSocketAddress(8888));
//获取连接
//Future里面放的就是方法的结果
//********同步********
System.out.println("准备连接客户端");
Future<AsynchronousSocketChannel> future = assc.accept();
//Future方法需要调用get()方法获取真正的返回值
AsynchronousSocketChannel sc = future.get();
System.out.println("连接上了客户端");
//读取客户端发来的数据
ByteBuffer buffer = ByteBuffer.allocate(1024);
//读取
//以前返回的是读取到的个数,真正的个数就在Future里面放着
//********同步********
System.out.println("准备读取数据");
Future<Integer> future2 = sc.read(buffer);
//获取真正的返回值
Integer len = future2.get();
System.out.println("读取到了数据");
//打印
System.out.println(new String(buffer.array(),0,len));
}
实操–AIO 异步非阻塞连接和异步读
需求
- 实现异步连接,异步读
分析
- 获取AsynchronousServerSocketChannel对象,绑定端口
- 异步接收客户端请求
- 在CompletionHandler的completed方法中异步读数据
实现
- 服务器端代码:
//异步非阻塞连接和读取
public static void main(String[] args) throws IOException {
//创建对象
AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open();
//绑定端口
assc.bind(new InetSocketAddress(8000));
//异步非阻塞连接!!!!
//第一个参数是一个附件
System.out.println(1);
assc.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
public void completed(AsynchronousSocketChannel s, Object attachment) {
//如果连接客户端成功,应该获取客户端发来的数据
//completed()的第一个参数表示的是Socket对象.
System.out.println(5);
//创建数组
ByteBuffer buffer = ByteBuffer.allocate(1024);
//异步非阻塞读!!!!!
System.out.println(3);
s.read(buffer, null, new CompletionHandler<Integer, Object>() {
@Override
public void completed(Integer len, Object attachment) {
//读取成功会自动调用这个方法
//completed()方法的第一个参数是read()读取到的实际个数
//打印数据
System.out.println(6);
System.out.println(new String(buffer.array(),0,len));
}
@Override
public void failed(Throwable exc, Object attachment) {
}
});
System.out.println(4);
}
@Override
public void failed(Throwable exc, Object attachment) {
}
});
System.out.println(2);
//让程序别结束写一个死循环
while(true){
}
}
总结:
NIO的优点
读写效率高,多路复用,主要处理高并发操作