AIO简介
Java AIO 是Java 7发布的新特性,支持异步方式处理消息,被称为NIO2.0版本,是NIO的增强版。AIO为异步非阻塞IO,通过接收IO完成后的通知的方式进行结果处理。java中的AIO依赖于系统提供的异步非阻塞IO模型,底层使用AIO的系统调用进行IO请求及通知接收(关于AIO模型见Linux五种网络IO模型)
AIO工具类
Java网络IO中的AIO实现主要借助AsynchronousServerSocketChannel、AsynchronousSocketChannel、CompletionHandler、AsynchronousChannelGroup,这几个类均位于java.nio包,下面对各自相关使用简单介绍一下。
AsynchronousChannelGroup
用于资源共享的一组异步通道,异步通道组封装了处理绑定到组的asynchronous channels启动的I / O操作完成所需的机制。 组具有关联的线程池,任务将提交到该线程池以处理I / O事件,并分派给completion-handlers ,该队列消耗对组中通道执行的异步操作的结果。 除了处理I / O事件之外,池化线程还可以执行支持异步I / O操作执行所需的其他任务。
构造方式
public static AsynchronousChannelGroup withCachedThreadPool(ExecutorService executor,int initialSize)
创建具有给定线程池的异步通道组,该线程池根据需要创建新线程。
public static AsynchronousChannelGroup withFixedThreadPool(int nThreads,ThreadFactory threadFactory)
创建具有固定线程池的异步通道组。
public static AsynchronousChannelGroup withThreadPool(ExecutorService executor)
创建给定线程池的异步通道组
常用方法
void shutdown():
标记当前组被关闭,后续有异步套接字通道绑定本组会抛出异常,当所有的套接字请求执行完毕后,该组会被正式关闭。
** void shutdownNow()**
立即关闭当前组,所有套接字通道被关闭,但不会中断当前当前接收完数据正在进行处理的线程。当所有执行CompleteHandler的线程执行完毕后正式关闭。
** boolean awaitTermination(long timeout, TimeUnit unit)**
阻塞等待关闭当前组。
AsynchronousServerSocketChannel
AsynchronousServerSocketChannel对应于BIO中的ServerSocketChannel,用于服务端接收连接,建立套接字。进行异步处理。
构造方式
与ServerSocketChannel相似,使用静态方法进行构造。
static AsynchronousServerSocketChannel open(AsynchronousChannelGroup group)
绑定指定异步套接字通道组构造服务端异步套接字通道,
static AsynchronousServerSocketChannel open()
使用默认异步套接字通道组构建服务端异步套接字通道。
主要方法
AsynchronousServerSocketChannel bind(SocketAddress local, int backlog)
绑定本地套接字地址,指定等待建立连接队列的长度为backlog,设置值需大于0,小于等于0时为默认值50。
AsynchronousServerSocketChannel bind(SocketAddress local)
绑定本地套接字,此时等待建立连接队列的长度为默认值50
void accept(A attachment,CompletionHandler<AsynchronousSocketChannel,? super A> handler);
接收套接字请求,请求数据接收完成后交于CompletionHandler对象实例handler处理,同时将泛型参数Attachment传递给handler用于识别上下文。
Future accept()
接收套接字请求,获取Future对象,接收到新连接后通过Future对象获取AsynchronousSocketChannel对象。
accept()与accept(A attachment,CompletionHandler<AsynchronousSocketChannel,? super A> handler)区别
首先看一下Future接口
public interface Future<V> {
/**
* Attempts to cancel execution of this task. This attempt will
* fail if the task has already completed, has already been cancelled,
* or could not be cancelled for some other reason. If successful,
* and this task has not started when {@code cancel} is called,
* this task should never run. If the task has already started,
* then the {@code mayInterruptIfRunning} parameter determines
* whether the thread executing this task should be interrupted in
* an attempt to stop the task.
*
* <p>After this method returns, subsequent calls to {@link #isDone} will
* always return {@code true}. Subsequent calls to {@link #isCancelled}
* will always return {@code true} if this method returned {@code true}.
*
* @param mayInterruptIfRunning {@code true} if the thread executing this
* task should be interrupted; otherwise, in-progress tasks are allowed
* to complete
* @return {@code false} if the task could not be cancelled,
* typically because it has already completed normally;
* {@code true} otherwise
*/
boolean cancel(boolean mayInterruptIfRunning);
/**
* Returns {@code true} if this task was cancelled before it completed
* normally.
*
* @return {@code true} if this task was cancelled before it completed
*/
boolean isCancelled();
/**
* Returns {@code true} if this task completed.
*
* Completion may be due to normal termination, an exception, or
* cancellation -- in all of these cases, this method will return
* {@code true}.
*
* @return {@code true} if this task completed
*/
boolean isDone();
/**
* Waits if necessary for the computation to complete, and then
* retrieves its result.
*
* @return the computed result
* @throws CancellationException if the computation was cancelled
* @throws ExecutionException if the computation threw an
* exception
* @throws InterruptedException if the current thread was interrupted
* while waiting
*/
V get() throws InterruptedException, ExecutionException;
/**
* Waits if necessary for at most the given time for the computation
* to complete, and then retrieves its result, if available.
*
* @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument
* @return the computed result
* @throws CancellationException if the computation was cancelled
* @throws ExecutionException if the computation threw an
* exception
* @throws InterruptedException if the current thread was interrupted
* while waiting
* @throws TimeoutException if the wait timed out
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
再观察一下CompletionHandler接口
public interface CompletionHandler<V,A> {
/**
* Invoked when an operation has completed.
*
* @param result
* The result of the I/O operation.
* @param attachment
* The object attached to the I/O operation when it was initiated.
*/
void completed(V result, A attachment);
/**
* Invoked when an operation fails.
*
* @param exc
* The exception to indicate why the I/O operation failed
* @param attachment
* The object attached to the I/O operation when it was initiated.
*/
void failed(Throwable exc, A attachment);
可以看到Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如有必要,计算完成前可以阻塞此方法。取消则由 cancel 方法来执行,而CompletionHandler提供的方法则简单直接,通过自定义对象的方式接收任务结束完成时的回调方法(completed或failed)。单纯使用Future时需要主动轮询任务结果或者阻塞获取结果,而CompletionHandler则采用被动回调的方式获取,因此更推荐使用CompletionHandler。
CompletionHandler
如上文所述,CompletionHandler是一个泛型接口,用于接收AIO异步回调。其中V未回调结果类型,A为连接附带参数attachment。
AIO通信实践
服务端
/**
* @author Mr.m
* 2020/12/11
*/
public class AsynchronousServer {
public static void main(String[] args) throws Exception {
AsynchronousChannelGroup asynchronousChannelGroup = AsynchronousChannelGroup.withCachedThreadPool(Executors.newCachedThreadPool(), 10);
AsynchronousServerSocketChannel asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open(asynchronousChannelGroup);
asynchronousServerSocketChannel.bind(new InetSocketAddress(8889));
Future<AsynchronousSocketChannel> asynchronousSocketChannelFuture;
while (true) {
asynchronousSocketChannelFuture = asynchronousServerSocketChannel.accept();
System.out.println("阻塞接收连接...");
AsynchronousSocketChannel asynchronousSocketChannel = asynchronousSocketChannelFuture.get();
System.out.println("接收请求:" + asynchronousSocketChannel.getRemoteAddress().toString());
new AsynchronousChannelConnectionHandler(asynchronousSocketChannel).handle();
}
}
}
基础实现比较简单,构造一个AsynchronousServerSocketChannel对象,死循环接收客户端连接。连接建立后交由AsynchronousChannelConnectionHandler 处理,AsynchronousChannelConnectionHandler 代码如下:
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
/**
* @author Mr.m
* 2021/1/4
*/
public class AsynchronousChannelConnectionHandler {
AsynchronousSocketChannel asynchronousSocketChannel;
public AsynchronousChannelConnectionHandler(AsynchronousSocketChannel asynchronousSocketChannel) {
this.asynchronousSocketChannel = asynchronousSocketChannel;
}
public void handle() {
final ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
asynchronousSocketChannel.read(byteBuffer, null, new CompletionHandler<Integer, Object>() {
@Override
public void completed(Integer result, Object attachment) {
if (result > 0) {
System.out.println("请求内容:" + new String(byteBuffer.array()));
String resp = "操作成功";
ByteBuffer writeBuffer = ByteBuffer.wrap(resp.getBytes());
asynchronousSocketChannel.write(writeBuffer);
}
}
@Override
public void failed(Throwable exc, Object attachment) {
}
});
}
}
AsynchronousChannelConnectionHandler会对请求内容读取,简单处理,并返回结果。
客户端
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.Future;
/**
* @author Mr.m
* 2021/1/4
*/
public class AioClient {
public static void main(String[] args) throws IOException, InterruptedException {
AsynchronousSocketChannel asynchronousSocketChannel = AsynchronousSocketChannel.open();
System.out.println("发起连接请求");
Future<Void> future = asynchronousSocketChannel.connect(new InetSocketAddress("localhost", 8889));
//阻塞等待连接建立
future.get();
String req = "hello ,我是" + asynchronousSocketChannel.getLocalAddress().toString();
ByteBuffer byteBuffer = ByteBuffer.allocate(req.getBytes().length);
byteBuffer.put(req.getBytes());
byteBuffer.flip();
System.out.println("开始写入请求内容");
asynchronousSocketChannel.write(byteBuffer, null, new CompletionHandler<Integer, Object>() {
@Override
public void completed(Integer result, Object attachment) {
System.out.println("请求内容传输完成");
ByteBuffer respBuffer = ByteBuffer.allocate(1024);
asynchronousSocketChannel.read(respBuffer, null, new CompletionHandler<Integer, Object>() {
@Override
public void completed(Integer result, Object attachment) {
String resp = new String(respBuffer.array());
System.out.println("收到回复:" + resp);
}
@Override
public void failed(Throwable exc, Object attachment) {
}
});
}
@Override
public void failed(Throwable exc, Object attachment) {
}
});
Thread.sleep(10000L);
}
}
客户端构建一个AsynchronousSocketChannel ,阻塞等待连接建立,异步写入请求内容,异步获取返回结果。