转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7794151.html

  前面讲到:Java IO编程全解(四)——NIO编程

  NIO2.0引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。异步通道提供两种方式获取操作结果。

  • 通过java.util.concurrent.Future类来表示异步操作的结果;
  • 在执行异步操作的时候传入一个java.nio.channels。

  CompletionHandler接口的实现类作为操作完成的回调。

  NIO2.0的异步套接字通道是真正的异步非阻塞I/O,它对UNIX网络编程中的事件驱动I/O(AIO),它不需要通过多路复用器(Selector)对注册的通道进行轮询操作即可实现异步读写,从而简化了NIO的编程模型。

  下面通过代码来熟悉NIO2.0 AIO的相关类库,仍旧以时间服务器为例程进行讲解。

1.AIO创建的TimeServer源码分析

package joanna.yan.aio;

public class TimeServer {

    public static void main(String[] args) {
int port=9090;
if(args!=null&&args.length>0){
try {
port=Integer.valueOf(args[0]);
} catch (Exception e) {
// 采用默认值
}
} AsyncTimeServerHandler timeServer=new AsyncTimeServerHandler(port);
new Thread(timeServer,"AIO-AsyncTimeServerHandler-001").start();
}
}
package joanna.yan.aio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.util.concurrent.CountDownLatch; public class AsyncTimeServerHandler implements Runnable{
private int port;
CountDownLatch latch;
AsynchronousServerSocketChannel asynchronousServerSocketChannel; public AsyncTimeServerHandler(int port){
this.port=port;
try {
//创建一个异步的服务端通道AsynchronousServerSocketChannel
asynchronousServerSocketChannel=AsynchronousServerSocketChannel.open();
asynchronousServerSocketChannel.bind(new InetSocketAddress(port));
System.out.println("The time server is start in port: "+port);
} catch (IOException e) {
e.printStackTrace();
}
} @Override
public void run() {
/*
*初始化CountDownLatch对象。
*它的作用是,在完成一组正在执行的操作之前,允许当前的线程一直阻塞。
*在本例中,我们让线程在此阻塞,防止服务器执行完成退出。
*在实际项目应用中,不需要启动独立的线程来处理AsynchronousServerSocketChannel,这里仅仅是个demo演示。
*/
latch=new CountDownLatch(1);
doAccept();
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
} /**
* 接收客户端的连接。
* 由于这里是异步操作。我们可以传递一个CompletionHandler<AsynchronousSocketChannel,? super A>类型
* 的handler实例接收accept操作成功的通知消息
*/
public void doAccept() {
asynchronousServerSocketChannel.accept(this, new AcceptCompletionHandler());
}
}
package joanna.yan.aio;

import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
/**
* 接收accept操作成功的通知消息
* @author Administrator
*
*/
public class AcceptCompletionHandler implements CompletionHandler<AsynchronousSocketChannel,AsyncTimeServerHandler>{ @Override
public void completed(AsynchronousSocketChannel result,
AsyncTimeServerHandler attachment) {
/*
* 疑惑:既然已经接收客户端成功了,为什么还要再次调用accept方法呢?
* 原因:当我们调用AsynchronousServerSocketChannel的accept方法后,如果有新的客户端连接接入,
* 系统将回调我们传入的CompletionHandler实例的completed方法,表示新的客户端已经接入成功,
* 因为一个AsynchronousServerSocketChannel可以接收成千上万个客户端,所以我们需要继续调用它的accep方法,
* 接收其他的客户端连接,最终形成一个循环。
* 每当接收一个客户读连接成功之后,再异步接收新的客户端连接。
*/
attachment.asynchronousServerSocketChannel.accept(attachment, this);
ByteBuffer buffer=ByteBuffer.allocate(1024);
/*
* 参数一:接收缓冲区,用于从异步Channel中读取数据包;
* 参数二:异步Channel携带的附件,通知回调的时候作为入参使用;
* 参数三:接收通知回调的业务handler
*/
result.read(buffer, buffer, new ReadCompletionHandler(result));
} @Override
public void failed(Throwable exc, AsyncTimeServerHandler attachment) {
exc.printStackTrace();
attachment.latch.countDown();
}
}
package joanna.yan.aio;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.Date;
/**
* 主要用于读取半包消息和发送应答。
* 本例不对半包读写进行具体说明,在后面的Netty半包处理中会介绍。
* @author Administrator
*
*/
public class ReadCompletionHandler implements CompletionHandler<Integer, ByteBuffer>{ private AsynchronousSocketChannel channel; public ReadCompletionHandler(AsynchronousSocketChannel channel){
if(this.channel==null){
this.channel=channel;
}
} @Override
public void completed(Integer result, ByteBuffer attachment) {
//为后续从缓冲区读取数据做准备
attachment.flip();
byte[] body=new byte[attachment.remaining()];
attachment.get(body); try {
String req=new String(body, "UTF-8");
System.out.println("The time server receive order : "+req);
String currentTime="QUERY TIME ORDER".equalsIgnoreCase(req) ?
new Date(System.currentTimeMillis()).toString() : "BAD ORDER";
doWrite(currentTime);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} /**
* 发生异常的时候调用。
* 对异常Throwable进行判断,如果是I/O异常,就关闭链路,释放资源;
* 如果是其它异常,按照业务自己的逻辑进行处理。
* 本例作为简单demo,没有对异常进行分类判断,只要发生了读写异常,就关闭链路,释放资源。
*/
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
try {
this.channel.close();
} catch (IOException e) {
e.printStackTrace();
}
} private void doWrite(String currentTime) {
if(currentTime!=null&&currentTime.trim().length()>0){
byte[] bytes=currentTime.getBytes();
ByteBuffer writeBuffer=ByteBuffer.allocate(bytes.length);
writeBuffer.put(bytes);
writeBuffer.flip(); channel.write(writeBuffer, writeBuffer, new CompletionHandler<Integer, ByteBuffer>() { @Override
public void completed(Integer result, ByteBuffer buffer) {
//如果没有发送完成,继续发送
if(buffer.hasRemaining()){
channel.write(buffer, buffer, this);
}
} @Override
public void failed(Throwable exc, ByteBuffer attachment) {
try {
channel.close();
} catch (IOException e) {
//ingnore on close
}
}
});
}
}
}

2.AIO创建的TimeClient源码分析

package joanna.yan.aio;

public class TimeClient {
public static void main(String[] args) {
int port=9090;
if(args!=null&&args.length>0){
try {
port=Integer.valueOf(args[0]);
} catch (Exception e) {
// 采用默认值
}
} /*
* 通过一个独立的I/O线程创建异步时间服务器客户端handler。
* 在实际项目中,我们不需要独立的线程创建异步连接对象,因为底层都是通过JDK的系统回调实现的。
*/
new Thread(new AsyncTimeClientHandler("127.0.0.1", port),"AIO-AsyncTimeClientHandle-001").start(); /*
* 需要指出的是,正如之前的NIO例程,我们并没有完整的处理网络的半包读写,在对例程进行功能测试的是还没有问题,
* 但是,如果对代码稍加改造,进行压力或者性能测试,就会发现输出结果存在问题。
* 这里只集中将NIO的入门知识,后面会详细讲到半包读写
*/
}
}
package joanna.yan.aio;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.CountDownLatch; public class AsyncTimeClientHandler implements CompletionHandler<Void, AsyncTimeClientHandler>,Runnable{ private AsynchronousSocketChannel client;
private String host;
private int port;
private CountDownLatch latch; public AsyncTimeClientHandler(String host,int port){
this.host=host;
this.port=port;
try {
client=AsynchronousSocketChannel.open();
} catch (IOException e) {
e.printStackTrace();
}
} @Override
public void completed(Void result, AsyncTimeClientHandler attachment) {
byte[] req="QUERY TIME ORDER".getBytes();
ByteBuffer writeBuffer=ByteBuffer.allocate(req.length);
writeBuffer.put(req);
writeBuffer.flip();
client.write(writeBuffer, writeBuffer, new CompletionHandler<Integer, ByteBuffer>() {//用于写操作完成后的回调 @Override
public void completed(Integer result, ByteBuffer buffer) {
if(buffer.hasRemaining()){
client.write(buffer, buffer, this);
}else{
ByteBuffer readBuffer=ByteBuffer.allocate(1024);
client.read(readBuffer, readBuffer, new CompletionHandler<Integer, ByteBuffer>(){//当读取完成被JDK回调时,构造应答消息。 @Override
public void completed(Integer result,
ByteBuffer buffer) {
buffer.flip();
byte[] bytes=new byte[buffer.remaining()];
buffer.get(bytes);
String body;
try {
body=new String(bytes, "UTF-8");
System.out.println("Now is : "+body);
latch.countDown();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} @Override
public void failed(Throwable exc, ByteBuffer attachment) {
try {
client.close();
//让AsyncTimeClientHandler线程执行完毕,客户端退出执行
latch.countDown();
} catch (IOException e) {
//ingnore on close
}
}
});
}
} @Override
public void failed(Throwable exc, ByteBuffer attachment) {
try {
client.close();
latch.countDown();
} catch (IOException e) {
//ingnore on close
}
}
});
} @Override
public void failed(Throwable exc, AsyncTimeClientHandler attachment) {
exc.printStackTrace();
try {
client.close();
latch.countDown();
} catch (IOException e) {
//ingnore on close
}
} @Override
public void run() {
//创建CountDownLatch进行等待,防止异步操作没有执行完成线程就退出。
latch=new CountDownLatch(1);
/*
* 参数二:AsynchronousSocketChannel的附件,用于回调通知时作为入参被传递,调用者可以自定义
* 参数三:异步参数回调通知接口,由调用者实现。
*/
client.connect(new InetSocketAddress(host, port), this, this);
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

  异步SocketChannel是被动执行对象,我们不需要像NIO编程那样创建一个独立I/O线程来处理读写操作。对于AsynchronousServerSocketChannel和 AsynchronousSocketChannel,它们都由JDK底层的线程池负责回调并驱动读写操作。正因为如此,基于NIO2.0新的异步非阻塞Channel进行编程比NIO编程更为简单。

  后面,我们将对前面讲到的4种I/O进行概念澄清和比较,让大家从整体上掌握这些I/O模型的差异。以便在未来的工作中能够根据产品的实际情况选择合适的I/O模型。

Java IO编程全解(六)——4种I/O的对比与选型

如果此文对您有帮助,微信打赏我一下吧~

Java IO编程全解(五)——AIO编程的更多相关文章

  1. Java IO编程全解(六)——4种I/O的对比与选型

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7804185.html 前面讲到:Java IO编程全解(五)--AIO编程 为了防止由于对一些技术概念和术语 ...

  2. Java IO编程全解(四)——NIO编程

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7793964.html 前面讲到:Java IO编程全解(三)——伪异步IO编程 NIO,即New I/O,这 ...

  3. Java IO编程全解(一)——Java的I/O演进之路

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7419117.html JDK1.4之前的早期版本,Java对I/O的支持并不完善,开发人员在开发高性能I/O ...

  4. Java IO编程全解(三)——伪异步IO编程

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7723174.html 前面讲到:Java IO编程全解(二)--传统的BIO编程 为了解决同步阻塞I/O面临 ...

  5. Java IO编程全解(二)——传统的BIO编程

    前面讲到:Java IO编程全解(一)——Java的I/O演进之路 网络编程的基本模型是Client/Server模型,也就是两个进程之间进行相互通信,其中服务端提供位置信息(绑定的IP地址和监听端口 ...

  6. #《JAVA程序设计》 20155214 实验五 网络编程与安全

    <JAVA程序设计> 20155214 实验五 网络编程与安全 实验内容 掌握Socket程序的编写: 掌握密码技术的使用: 设计安全传输系统. 实验要求 要求一 结对实现中缀表达式转后缀 ...

  7. JAVA IO 类库详解

    JAVA IO类库详解 一.InputStream类 1.表示字节输入流的所有类的超类,是一个抽象类. 2.类的方法 方法 参数 功能详述 InputStream 构造方法 available 如果用 ...

  8. Sql Server函数全解<五>之系统函数

    原文:Sql Server函数全解<五>之系统函数  系统信息包括当前使用的数据库名称,主机名,系统错误消息以及用户名称等内容.使用SQL SERVER中的系统函数可以在需要的时候获取这些 ...

  9. Java IO流详解(五)——缓冲流

    缓冲流也叫高效流,是处理流的一种,即是作用在流上的流.其目的就是加快读取和写入数据的速度. 缓冲流本身并没有IO功能,只是在别的流上加上缓冲效果从而提高了效率.当对文件或其他目标频繁读写或操作效率低, ...

随机推荐

  1. C++ static成员变量与static成员函数

    类中的静态成员真是个让人爱恨交加的特性.我决定好好总结一下静态类成员的知识点,以便自己在以后面试中,在此类问题上不在被动. 静态类成员包括静态数据成员和静态函数成员两部分.  一 静态数据成员:  类 ...

  2. 支持向量机(Support Vector Machine)-----SVM之SMO算法(转)

    此文转自两篇博文 有修改 序列最小优化算法(英语:Sequential minimal optimization, SMO)是一种用于解决支持向量机训练过程中所产生优化问题的算法.SMO由微软研究院的 ...

  3. Detect Capital

    Given a word, you need to judge whether the usage of capitals in it is right or not. We define the u ...

  4. 在CentOS6上利用PXE+Kickstart+Apache+DHCP实现无人值守安装

    在CentOS6上利用PXE+Kickstart+Apache+DHCP实现无人值守安装 1.PXEServer:OS:CentOS6.9IP:172.16.25.69: (1)apache:# mo ...

  5. 入门VMware Workstation下的Debian学习之Vim简单使用(三)

    什么是Vim? Vim具有程序编辑的能力,可以主动的以字体颜色辨别语法的正确性,方便程序设计. Vim是从vi发展出来的一个文本编辑器.代码补完.编译及错误跳转等方便编程的功能特别丰富,在程序员中被广 ...

  6. ch3-模板语法({{}} v-html v-bind:id 表达式 指令 修饰符 过滤器)

    1 模板语法 Vue.js 使用了基于 HTML 的模版语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据. 所有 Vue.js 的模板都是合法的 HTML ,所以能被遵循规范的浏览器 ...

  7. Weave Scope 容器地图 - 每天5分钟玩转 Docker 容器技术(80)

    Weave Scope 的最大特点是会自动生成一张 Docker 容器地图,让我们能够直观地理解.监控和控制容器.千言万语不及一张图,先感受一下. 下面开始实践 Weave Scope. 安装 执行如 ...

  8. 数据处理:12个使得效率倍增的pandas技巧

    数据处理:12个使得效率倍增的pandas技巧 1. 背景描述 Python正迅速成为数据科学家偏爱的语言,这合情合理.它拥有作为一种编程语言广阔的生态环境以及众多优秀的科学计算库.如果你刚开始学习P ...

  9. 扩展javascript扩展(类,对象,原型)

     扩展javascript扩展(类,对象,原型)

  10. WinForm 读写配置文件

    //读配置文件 方法(1) //ConfigurationManager.RefreshSection("appSettings"); //强制重新载入 string settin ...