NIO线程模型
什么是NIO线程模型?
上图是NIO的线程模型, 基于select实现, 这种线程模型的特点: 多条channel通过一个选择器和单挑线程绑定, 并且在这种编程模型中, Channel中相关业务逻辑不允许存在耗时的任务 , 如果一定会有耗时的逻辑, 请将它们放置到线程池中去运行, 因为这种模型虽然做到了非阻塞, 但是他并不是真正的异步编程, 任何channel上的任何耗时的操作, 都会拖垮这个选择器, 进而拖垮整条线程 , 这也是为啥它会被称为 同步非阻塞
什么是同步?
- 其一: 因为当channel中出现了耗时的操作时, 其他的channel不得不同步等待
- 其二: 从编码上看: NIO编程中 服务端的
select()
会同步等待选择器感兴趣的事件发生 - 其三: 从操作系统的角度上看, 程序使用的数据来自 网卡 -> 操作系统的内核缓冲区 -> 用户区, 当数据进入用户区后java程序便可以对其进行读写操作, 所谓同步就是: 数据进入用户区的过程中,NIO编程模型需要同步并不停的询问
NIO线程模型的的优点
NIO线程线程模型相对于传统的BIO来说, 最大的优势就是在于 NIO线程模型中单条线程可同时为N个用户(Channel)服务, 而BIO编程模型让人诟病的地方就是, 任何一个新连接接入, 服务器都得为他开启不止一条新的线程去运行它, 这种BIO系统中, 并发肯定不会很高
NIO适用场景:
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持
AIO (Asynchronous Input/Output )模型
什么是AIO?
AIO是(jdk1.7) 发行的 异步IO编程模型, 真正实现了异步IO, 基于Linux系统的 Epoll 机制实现
无论是NIO, 还是AIO底层都没有改变网络通信的基本步骤, 而是在这个基础上进行了一系类的升级
AIO的底层实现是由操作系统完成的, 数据在内核空间&用户空间的迁移, 我们在编写代码时也是这样, 只需要调用 AIO.read()
或者是 AIO.write()
即可, 换句话说, 我们的业务逻辑就成了 回调, 原来在操作系统处理数据的这个过程中, 我们的程序需要阻塞等待着, 亦或者放在线程池中运行, 而在AIO编程中这段等待时间差被省去了, 因为当操作系统认为数据还有没准备完时, 它是不会打扰我们的程序的, 这时我们的程序可以去处理其他的逻辑, 而一旦操作系统认为数据齐全了, 他就会回调我们的提供的回调函数
- 对应操作系统来说, 当有流数据可读时, 操作系统会将流传入到read方法的缓冲区, 然后回调相关的 CompletionHandler
- 对于写操作而言, 操作系统会将程序中Buffer里面数据写入到从用户空间写入到系统空间 再写入到网卡中, 写入完毕, 同样会回调相关的回调函数
AIO编程Server端的示例
下面贴出来一个AIO编程Server端的实例:
像下面的 read() write() accept() 全是异步的, 一经调用即刻返回, 不一样的地方是我们会提供一个回调对象, 留给操作系统, 当操作系统认为读写数据都到位了, 就会去回调这些函数
public class AIOServer {
private ExecutorService executorService;
// 服务端的Channel
private AsynchronousServerSocketChannel asynchronousServerSocketChannel;
private AIOServer(int port) {
init(port);
}
// 初始化
private void init(int port) {
System.out.println("aio server start with port " + port);
executorService = Executors.newFixedThreadPool(5);
try {
// 开启服务端的通道
asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open();
// 绑定端口
asynchronousServerSocketChannel.bind(new InetSocketAddress(port));
System.out.println("server start ... ");
/**
* 方法会异步的去接收一个请求, accept()同样是
* 参数1 : this , 暂时理解成任意类型的
* 参数2 : CompleteHandler -- 当请求到来后,会交付给 AIOServerHandler进行处理
*
* todo 在 AIO中的监听并不是while(true), 而是类似递归的操作, 每次监听到客户端的请求后, 都需要在处理逻辑中开启下一次的监听
*/
asynchronousServerSocketChannel.accept(this, new AIOServerHandler());
System.out.println("------------------------------");
// 阻塞程序
try {
TimeUnit.SECONDS.sleep(60);
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public AsynchronousServerSocketChannel getAsynchronousServerSocketChannel() {
return this.asynchronousServerSocketChannel;
}
public static void main(String[] args) {
AIOServer aioServer = new AIOServer(9999);
}
}
AIO的适用场景
AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
我是bloger 赐我白日梦, 欢迎关注我 --武汉加油