Java NIO开发需要注意的陷阱(转)
陷阱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开发需要注意的陷阱(转)的更多相关文章
- Netty精粹之JAVA NIO开发需要知道的
学习Netty框架以及相关源码也有一小段时间了,恰逢今天除夕,写篇文章总结一下.Netty是个高效的JAVA NIO框架,总体框架基于异步非阻塞的设计,基于网络IO事件驱动,主要贡献在于可以让用户基于 ...
- Java多线程:Linux多路复用,Java NIO与Netty简述
JVM的多路复用器实现原理 Linux 2.5以前:select/poll Linux 2.6以后: epoll Windows: IOCP Free BSD, OS X: kqueue 下面仅讲解L ...
- Java NIO服务器端开发
一.NIO类库简介 1.缓冲区Buffer Buffer是一个对象,包含一些要写入和读出的数据. 在NIO中,所有的数据都是用缓冲区处理的,读取数据时,它是从通道(Channel)直接读到缓冲区中,在 ...
- 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 ...
- 我的Java开发学习之旅------>Java NIO 报java.nio.charset.MalformedInputException: Input length = 1异常
今天在使用Java NIO的Channel和Buffer进行文件操作时候,报了java.nio.charset.MalformedInputException: Input length = 1异常, ...
- 5种调优Java NIO和NIO.2的方式
Java NIO(New Input/Output)——新的输入/输出API包——是2002年引入到J2SE 1.4里的.Java NIO的目标是提高Java平台上的I/O密集型任务的性能.过了十年, ...
- Java NIO浅析 转至 美团技术团队
出处: Java NIO浅析 NIO(Non-blocking I/O,在Java领域,也称为New I/O),是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,已经被越来越多地应用到大型应用服 ...
- Netty快速入门(05)Java NIO 介绍-Selector
Java NIO Selector Selector是Java NIO中的一个组件,用于检查一个或多个NIO Channel的状态是否处于可读.可写.如此可以实现单线程管理多个channels,也就是 ...
- Java NIO使用及原理分析(1-4)(转)
转载的原文章也找不到!从以下博客中找到http://blog.csdn.net/wuxianglong/article/details/6604817 转载自:李会军•宁静致远 最近由于工作关系要做一 ...
随机推荐
- 优秀前端工程师必备: 我要一个新窗口: js开新窗的2种姿势
1.<a href="https://www.cnblogs.com/" title="博客园">当前页面打开博客园</a> js代码等 ...
- linux每天一小步---cat命令详解
1 命令功能 cat命令是linux系统下的一个文本输出命令,通常用于查看文件的内容. 2 命令语法 cat [选项参数] 文件名 3 命令参数 -n 由1开始对所有输出的内容行数编号 -b ...
- Zynq学习笔记(1)——Hellow World
Zynq是一款SOC芯片,之前只是用了PL(Programmable Logic)部分,而Zynq最突出的功能,就是内部的双核Cortex-A9,所以从现在开始我将学习ZYNQ的SOC学习(PS部分) ...
- CURL命令测试网站打开速度
curl -o /dev/null -s -w %{time_namelookup}:%{time_connect}:%{time_starttransfer}:%{time_total} http: ...
- 关于fastjson的一个坑:输出json时,bean对象属性首字母默认被小写
fastjson 是一个性能很好的 Java 语言实现的 JSON 解析器和生成器,来自阿里巴巴. 主要特点: 快速FAST: 比其它任何基于Java的解析器和生成器更快,包括jackson 强大:支 ...
- 获取web项目中的webroot目录路径
备忘,一段代码: @Override public void init(FilterConfig arg0) throws ServletException { // TODO Auto-genera ...
- ajax +LoadLayer插件实现访问页面跳转loading..
布局页:第一步进行扩展ajax$(function () { $.ajax2 = function (options) {//遮罩 Mask();//jquery 原生ajax $.ajax(opti ...
- 1-初步了解C#-语言基础
本篇博客对应视频讲解 前言 终于开始讲语言了,我选择讲C#.为什么呢?因为C#是很好的入门语言,简洁.全面,面向对象类型安全,开发体验好,上手容易.而其他的语言也已经有人讲了很多了,C#相对来说要少一 ...
- MyBatis高级及其SSM框架搭建
代码生成器 首先meaven项目中导入支持包 <dependencies> <!-- https://mvnrepository.com/artifact/mysql/mysql-c ...
- Visual Studio code安装步骤
1.官方下载:https://code.visualstudio.com/,本人电脑是window系统 下载之后,双击安装,安装完之后左侧栏那边是英文,如何变为中文: 按快捷键ctrl+shift+ ...