陷阱1:处理事件忘记移除key
在select返回值大于0的情况下,循环处理
Selector.selectedKeys集合,每处理一个必须从Set中移除

Iterator<SelectionKey> it=set.iterator();
While(it.hasNext()){
SelectionKey key=it.next();
it.remove(); //切记移除
„„处理事件
}

不移除的后果是本次的就绪的key集合下次会再次返回,导致无限循环,CPU消耗100%

陷阱2:Selector返回的key集合非线程安全

Selector.selectedKeys/keys 返回的集合都是非线程安全的
Selector.selectedKeys返回的可移除
Selector.keys 不可变
对selected keys的处理必须单线程处理或者适当同步

陷阱3:正确注册Channel和更新interest
直接注册不可吗?
channel.register(selector, ops, attachment);
不是不可以,效率问题
至少加两次锁,锁竞争激烈
Channel本身的regLock,竞争几乎没有
Selector内部的key集合,竞争激烈
更好的方式:加入缓冲队列,等待注册,reactor单线程处理

If(isReactorThread()){
channel.register(selector,ops,attachment);
}
else{
register.offer(newEvent(channel,ops,attachment));
selector.wakeup();
}

同样,SelectionKey.interest(ops)
在linux上会阻塞,需要获取selector内部锁做同步
在win32上不会阻塞
屏蔽平台差异,避免锁的激烈竞争,采用类似注册channel的方式:

if (this.isReactorThread()) {
key.interestOps(key.interestOps() | SelectionKey.OP_READ);
}
else {
this.register.offer(new Event(key,SelectionKey.OP_READ));
selector.wakeup();
}

陷阱4:正确处理OP_WRITE
OP_WRITE处理不当很容易导致CPU 100%
OP_WRITE触发条件:
   前提:interest了OP_WRITE
   触发条件:
        socket发送缓冲区可写
        远端关闭
        有错误发生
正确的处理方式:
   仅在已经连接的channel上注册
   仅在有数据可写的时候才注册
   触发之后立即取消注册,否则会继续触发导致循环
   处理完成后视情况决定是否继续注册
     没有完全写入,继续注册
     全部写入,无需注册

陷阱5:正确取消注册channel
SelectableChannel一旦注册将一直有效直到明确取消
怎么取消注册?
   channel.close(),内部会调用key.cancel()
   key.cancel();
   中断channel的读写所在线程引起的channel关闭
但是这样还不够!
   key.cancel()仅仅是将key加入cancelledKeys
   直到下一次select才真正处理
   并且channel的socketfd只有在真正取消注册后才会close(fd)

后果是什么?
  服务端,问题不大,select调用频繁
  客户端,通常只有一个连接,关闭channel之后,没有调用select就关闭了selector
  sockfd没有关闭,停留在CLOSE_WAIT状态
正确的处理方式,取消注册也应当作为事件交给reactor处理,及时wakeup做select
适当的时候调用selector.selectNow()
  Netty在超过256连接关闭的时候主动调用一次selectNow

static final int CLEANUP_INTERVAL=256;
private boolean cleanUpCancelledKeys()throws IOException{
if(cancelledKeys>=CLEANUP_INTERVAL){
cancelledKeys=0;
selector.selectNow();
returntrue;
}
returnfalse;
}
//channel关闭的时候
channel.socket.close();
cancelledKeys++;

陷阱6:同时注册OP_ACCPET和OP_READ,同时注册OP_CONNECT和OP_WRITE
在底层来说,只有两种事件:read和write
Java NIO还引入了OP_ACCEPT和OP_CONNECT
  OP_ACCEPT、OP_READ == Read
  OP_CONNECT、OP_WRITE == Write
同时注册OP_ACCEPT和OP_READ ,或者同时注册OP_CONNECT和OP_WRITE在不同平台上产生错误的行为,避免这样做!

陷阱7:正确处理connect
SocketChannel.connect方法在非阻塞模式下可能返回false,切记判断返回值
    如果是loopback连接,可能直接返回true,表示连接成功
    返回false,后续处理
       注册channel到selector,监听OP_CONNECT事件
       在OP_CONNECT触发后,调用SocketChannel.finishConnect成功后,连接才真正建立
陷阱:
    没有判断connect返回值
    没有调用finishConnect
    在OP_CONNECT触发后,没有移除OP_CONNECT,导致SelectionKey一直处于就绪状态,空耗CPU
       OP_CONNECT只能在还没有连接的channel上注册

忠告

尽量不要尝试实现自己的nio框架,除非有经验丰富的工程师
尽量使用经过广泛实践的开源NIO框架Mina、Netty3、xSocket
尽量使用最新稳定版JDK
遇到问题的时候,也许你可以先看下java的bug database

Java NIO开发需要注意的陷阱(转)的更多相关文章

  1. Netty精粹之JAVA NIO开发需要知道的

    学习Netty框架以及相关源码也有一小段时间了,恰逢今天除夕,写篇文章总结一下.Netty是个高效的JAVA NIO框架,总体框架基于异步非阻塞的设计,基于网络IO事件驱动,主要贡献在于可以让用户基于 ...

  2. Java多线程:Linux多路复用,Java NIO与Netty简述

    JVM的多路复用器实现原理 Linux 2.5以前:select/poll Linux 2.6以后: epoll Windows: IOCP Free BSD, OS X: kqueue 下面仅讲解L ...

  3. Java NIO服务器端开发

    一.NIO类库简介 1.缓冲区Buffer Buffer是一个对象,包含一些要写入和读出的数据. 在NIO中,所有的数据都是用缓冲区处理的,读取数据时,它是从通道(Channel)直接读到缓冲区中,在 ...

  4. Eclipse下Android开发错误之Unable to execute dex: java.nio.BufferOverflowException. Check the Eclipse log for stack trace

    升级了Android版本后,在运行应用时提示: [2013-11-27 10:37:35 - Dex Loader] Unable to execute dex: java.nio.BufferOve ...

  5. 我的Java开发学习之旅------>Java NIO 报java.nio.charset.MalformedInputException: Input length = 1异常

    今天在使用Java NIO的Channel和Buffer进行文件操作时候,报了java.nio.charset.MalformedInputException: Input length = 1异常, ...

  6. 5种调优Java NIO和NIO.2的方式

    Java NIO(New Input/Output)——新的输入/输出API包——是2002年引入到J2SE 1.4里的.Java NIO的目标是提高Java平台上的I/O密集型任务的性能.过了十年, ...

  7. Java NIO浅析 转至 美团技术团队

    出处: Java NIO浅析 NIO(Non-blocking I/O,在Java领域,也称为New I/O),是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,已经被越来越多地应用到大型应用服 ...

  8. Netty快速入门(05)Java NIO 介绍-Selector

    Java NIO Selector Selector是Java NIO中的一个组件,用于检查一个或多个NIO Channel的状态是否处于可读.可写.如此可以实现单线程管理多个channels,也就是 ...

  9. Java NIO使用及原理分析(1-4)(转)

    转载的原文章也找不到!从以下博客中找到http://blog.csdn.net/wuxianglong/article/details/6604817 转载自:李会军•宁静致远 最近由于工作关系要做一 ...

随机推荐

  1. 20155308 2016-2017-2 《Java程序设计》第8周学习总结

    20155308 2016-2017-2 <Java程序设计>第8周学习总结 教材学习内容总结 第十四章 NIO与NIO2 NIO(New IO)-from JDK1.4 NIO2 -fr ...

  2. ARPG游戏打击感相关的技术简单总结

    说好的技术总结,根据近期的工作总结一下体现游戏打击感相关的技术.一般arpg的游戏打击感除了场景的氛围的烘托,策划的数值:连击,奖励伤害数字的连贯积累反馈,硬直加物理击飞ragdoll,更不可忽视的也 ...

  3. 如何在Mirth Connect中创建和调用自定义Java代码

    0-前言 本文章将向您展示如何创建自定义Java类,将其编译/打包到JAR中,将其包含在Mirth Connect在,并在JavaScript中调用它,您可以从任何JavaScript上下文调用自定义 ...

  4. MSP430 G2553 基本时钟模块+ (Basic Clock Module+)

    一.时钟源 MSP430的Basic Clock Module+支持的时钟源有: DCOCLK:内部数字控制振荡器,Internal digitally contrlled oscillator.所有 ...

  5. 关于createTextRange和createRange的一些用法【转】

    一.返回createTextRange的text和htmlText <mce:script language="javascript"><!--function ...

  6. 新手上路,django学习笔记(1) 环境部署

    很多年没写代码了,以前学的C#,用ASP.NET,但是最近几年没落了,JAVA在崛起,最近感觉Python比较火,总是在各种技术场合听到Python,或者身边的朋友在讨论Python,所以突然想学习一 ...

  7. 手动安装httpd服务器

    首先安装apr(Apache Portable Runtime) apr-util apr-iconv 安装之前需要 前置知识: 自己手动编译安装的软件的安装位置: /usr/local bin, s ...

  8. 3.C#WebAPI设置路由和参数2

    1.上面已经教大家如何修改全局路由了,那么修改完后我们在post请求的要这样使用,其中model模型我就默认你应该已经建好了,没有创建的话请看上一部分 Post方法的参数,如果提交的请求体需要是pho ...

  9. Devexpress GridControl无限高度惹得祸

    异常提示: issue, place the grid into a container that will give a finite height to the grid, or manually ...

  10. C#——做一个简单代理IP池

    一.缘由. 抓取数据时,有一些网站 设置了一些反爬虫设置,进而将自己本地 IP 地址拉入系统黑名单.从而达到禁止本地 IP 访问数据的请求. 二.思路. 根据其他 代理 IP 网站,进行一个免费的代理 ...