Java NIO使用及原理分析 (四)
在上一篇文章中介绍了关于缓冲区的一些细节内容,现在终于可以进入NIO中最有意思的部分非阻塞I/O。通常在进行同步I/O操作时,如果读取数据,代码会阻塞直至有 可供读取的数据。同样,写入调用将会阻塞直至数据能够写入。传统的Server/Client模式会基于TPR(Thread per Request),服务器会为每个客户端请求建立一个线程,由该线程单独负责处理一个客户请求。这种模式带来的一个问题就是线程数量的剧增,大量的线程会增大服务器的开销。大多数的实现为了避免这个问题,都采用了线程池模型,并设置线程池线程的最大数量,这由带来了新的问题,如果线程池中有200个线程,而有200个用户都在进行大文件下载,会导致第201个用户的请求无法及时处理,即便第201个用户只想请求一个几KB大小的页面。传统的
Server/Client模式如下图所示:
NIO中非阻塞I/O采用了基于Reactor模式的工作方式,I/O调用不会被阻塞,相反是注册感兴趣的特定I/O事件,如可读数据到达,新的套接字连接等等,在发生特定事件时,系统再通知我们。NIO中实现非阻塞I/O的核心对象就是Selector,Selector就是注册各种I/O事件地 方,而且当那些事件发生时,就是这个对象告诉我们所发生的事件,如下图所示:
从图中可以看出,当有读或写等任何注册的事件发生时,可以从Selector中获得相应的SelectionKey,同时从 SelectionKey中可以找到发生的事件和该事件所发生的具体的SelectableChannel,以获得客户端发送过来的数据。关于 SelectableChannel的可以参考Java
NIO使用及原理分析(一)
使用NIO中非阻塞I/O编写服务器处理程序,大体上可以分为下面三个步骤:
1. 向Selector对象注册感兴趣的事件
2. 从Selector中获取感兴趣的事件
3. 根据不同的事件进行相应的处理
接下来我们用一个简单的示例来说明整个过程。首先是向Selector对象注册感兴趣的事件:
- /*
- * 注册事件
- * */
- protected Selector getSelector() throws IOException {
- // 创建Selector对象
- Selector sel = Selector.open();
- // 创建可选择通道,并配置为非阻塞模式
- ServerSocketChannel server = ServerSocketChannel.open();
- server.configureBlocking(false);
- // 绑定通道到指定端口
- ServerSocket socket = server.socket();
- InetSocketAddress address = new InetSocketAddress(port);
- socket.bind(address);
- // 向Selector中注册感兴趣的事件
- server.register(sel, SelectionKey.OP_ACCEPT);
- return sel;
- }
创建了ServerSocketChannel对象,并调用configureBlocking()方法,配置为非阻塞模式,接下来的三行代码把该通道绑定到指定端口,最后向Selector中注册事件,此处指定的是参数是OP_ACCEPT,即指定我们想要监听accept事件,也就是新的连接发 生时所产生的事件,对于ServerSocketChannel通道来说,我们唯一可以指定的参数就是OP_ACCEPT。
从Selector中获取感兴趣的事件,即开始监听,进入内部循环:
- /*
- * 开始监听
- * */
- public void listen() {
- System.out.println("listen on " + port);
- try {
- while(true) {
- // 该调用会阻塞,直到至少有一个事件发生
- selector.select();
- Set<SelectionKey> keys = selector.selectedKeys();
- Iterator<SelectionKey> iter = keys.iterator();
- while (iter.hasNext()) {
- SelectionKey key = (SelectionKey) iter.next();
- iter.remove();
- process(key);
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
在非阻塞I/O中,内部循环模式基本都是遵循这种方式。首先调用select()方法,该方法会阻塞,直到至少有一个事件发生,然后再使用selectedKeys()方法获取发生事件的SelectionKey,再使用迭代器进行循环。
最后一步就是根据不同的事件,编写相应的处理代码:
- /*
- * 根据不同的事件做处理
- * */
- protected void process(SelectionKey key) throws IOException{
- // 接收请求
- if (key.isAcceptable()) {
- ServerSocketChannel server = (ServerSocketChannel) key.channel();
- SocketChannel channel = server.accept();
- channel.configureBlocking(false);
- channel.register(selector, SelectionKey.OP_READ);
- }
- // 读信息
- else if (key.isReadable()) {
- SocketChannel channel = (SocketChannel) key.channel();
- int count = channel.read(buffer);
- if (count > 0) {
- buffer.flip();
- CharBuffer charBuffer = decoder.decode(buffer);
- name = charBuffer.toString();
- SelectionKey sKey = channel.register(selector, SelectionKey.OP_WRITE);
- sKey.attach(name);
- } else {
- channel.close();
- }
- buffer.clear();
- }
- // 写事件
- else if (key.isWritable()) {
- SocketChannel channel = (SocketChannel) key.channel();
- String name = (String) key.attachment();
- ByteBuffer block = encoder.encode(CharBuffer.wrap("Hello " + name));
- if(block != null)
- {
- channel.write(block);
- }
- else
- {
- channel.close();
- }
- }
- }
此处分别判断是接受请求、读数据还是写事件,分别作不同的处理。
Java NIO使用及原理分析 (四)的更多相关文章
- Java NIO使用及原理分析 (四)(转)
在上一篇文章中介绍了关于缓冲区的一些细节内容,现在终于可以进入NIO中最有意思的部分非阻塞I/O.通常在进行同步I/O操作时,如果读取数据,代码会阻塞直至有 可供读取的数据.同样,写入调用将会阻塞直至 ...
- Java NIO使用及原理分析(二)
在第一篇中,我们介绍了NIO中的两个核心对象:缓冲区和通道,在谈到缓冲区时,我们说缓冲区对象本质上是一个数组,但它其实是一个特殊的数组,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况,如 ...
- Java NIO使用及原理分析(二)(转)
在第一篇中,我们介绍了NIO中的两个核心对象:缓冲区和通道,在谈到缓冲区时,我们说缓冲区对象本质上是一个数组,但它其实是一个特殊的数组,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况,如 ...
- Java NIO使用及原理分析(1-4)(转)
转载的原文章也找不到!从以下博客中找到http://blog.csdn.net/wuxianglong/article/details/6604817 转载自:李会军•宁静致远 最近由于工作关系要做一 ...
- Java NIO使用及原理分析 (一)(转)
最近由于工作关系要做一些Java方面的开发,其中最重要的一块就是Java NIO(New I/O),尽管很早以前了解过一些,但并没有认真去看过它的实现原理,也没有机会在工作中使用,这次也好重新研究一下 ...
- Java NIO使用及原理分析(三)
在上一篇文章中介绍了缓冲区内部对于状态变化的跟踪机制,而对于NIO中缓冲区来说,还有很多的内容值的学习,如缓冲区的分片与数据共享,只读缓冲区等.在本文中我们来看一下缓冲区一些更细节的内容. 缓冲区的分 ...
- Java NIO使用及原理分析(三)(转)
在上一篇文章中介绍了缓冲区内部对于状态变化的跟踪机制,而对于NIO中缓冲区来说,还有很多的内容值的学习,如缓冲区的分片与数据共享,只读缓冲区等.在本文中我们来看一下缓冲区一些更细节的内容. 缓冲区的分 ...
- Java NIO使用及原理分析 (一)
http://blog.csdn.net/wuxianglong/article/details/6604817
- Java NIO wakeup实现原理
本文转载自Java NIO wakeup实现原理 导语 最近在阅读netty源码时,很好奇Java NIO中Selector的wakeup()方法是如何唤醒selector的,于是决定深扒一下wake ...
随机推荐
- skip index scan
官网对skip index scan的解释: Index skip scans improve index scans by nonprefix columns since it is often f ...
- [转]如何根据cpu的processor数来确定程序的并发线程数量
原文:http://blog.csdn.net/kirayuan/article/details/6321967 我们可以在cat 里面发现processor数量,这里的processor可以理解为逻 ...
- Kaggle Competition Past Solutions
Kaggle Competition Past Solutions We learn more from code, and from great code. Not necessarily alwa ...
- uva 1308 - Viva Confetti
这个题目的方法是将圆盘分成一个个圆环,然后判断这些圆环是否被上面的圆覆盖: 如果这个圆的圆周上的圆弧都被上面的覆盖,暂时把它标记为不可见: 然后如果他的头上有个圆,他有个圆弧可见,那么他自己本身可见, ...
- Mysql大表查询优化技巧总结及案例分析
http://www.169it.com/article/3219955334.html sql语句使用基本原则:1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 orde ...
- Docker日志自动化: ElasticSearch、Logstash、Kibana以及Logspout
http://www.open-open.com/lib/view/open1432107136989.html
- 读书笔记-----Java并发编程实战(二)对象的共享
public class NoVisibility{ private static boolean ready; private static int number; private static c ...
- 《鸟哥的Linux私房菜》读书笔记一
1.CPU为一个具有特定功能的芯片,里面含有微指令集,一个CPU又可以分为两个主要的单元:算术逻辑单元和控制单元.CPU读取的数据都是从内存读取来的,内存内的数据是从输入单元传输来的.CPU处理完也要 ...
- 远程仓库版本回退方法 good
1 简介 最近在使用git时遇到了远程分支需要版本回滚的情况,于是做了一下研究,写下这篇博客. 2 问题 如果提交了一个错误的版本,怎么回退版本? 如果提交了一个错误的版本到远程分支,怎么回退远程分支 ...
- [LeetCode#156] Binary Tree Upside Down
Problem: Given a binary tree where all the right nodes are either leaf nodes with a sibling (a left ...