转载请注明出处: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. Cow Uncle 学习了叉积的一点运用,叉积真的不错

    Cow Uncle Time Limit: 4000/2000MS (Java/Others) Memory Limit: 128000/64000KB (Java/Others) SubmitSta ...

  2. Collecting Bugs poj2096 概率DP

                                                                Collecting Bugs Time Limit: 10000MS   Me ...

  3. Gaussian and Truncated Gaussian

    Everybody knows about Gaussian distribution, and Gaussian is very popular in Bayesian world and even ...

  4. 如何维护一个1000 IP的免费代理池

    楔子 好友李博士要买房了, 前几天应邀帮他抓链家的数据分析下房价, 爬到一半遇到了验证码. 李博士的想法是每天把链家在售的二手房数据都抓一遍, 然后按照时间序列分析. 链家线上在交易的二手房数据大概有 ...

  5. 使用SQLite做本地数据缓存的思考

    前言 在一个分布式缓存遍地都是的环境下,还讲本地缓存,感觉有点out了啊!可能大家看到标题,就没有想继续看下去的欲望了吧.但是,本地缓存的重要性也是有的! 本地缓存相比分布式缓存确实是比较out和比较 ...

  6. Python面试题之生成器/迭代器

    1.为什么要有生成器? 通过列表生成式,我们可以直接创建一个列表.但是,受到内存限制,列表容量肯定是有限的.而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个 ...

  7. git 忽略文件夹

    $ vim .gitignore 添加要忽略的文件或文件夹 esc + :wq 退出vim命令行

  8. Mysql安装后打开MySQL Command Line Client闪退解决方法

    1.开始菜单下;Mysql--->mysql server 5.6-->mysql command line Client ---右击,选择属性 2.在属性下查看目标位置: 3.将安装目录 ...

  9. python 设计模式,“多”例模式

    版本1:一个账号不能同时是司机乘客. #-*- coding:utf-8 -*- ''' Created on 2016年8月2日 @author: yangfanholiday ''' class ...

  10. 自己动手实现网络服务器(Web Server)——基于C#

    前言 最近在学习网络原理,突然萌发出自己实现一个网络服务器的想法,并且由于第三代小白机器人的开发需要,我把之前使用python.PHP写的那部分代码都迁移到了C#(别问我为什么这么喜欢C#),之前使用 ...