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 转载自:李会军•宁静致远 最近由于工作关系要做一 ...
随机推荐
- C语言 void 万能类型
C中 void类型其实是一种万能类型, 也就是说 我们的 void 可以代表任意一种类型,也就是说他的范围要比具体的一种类型要宽广, 例如: int *p = NULL;void *ptr = p; ...
- pointcloud(点云)与mesh(面元)模型的区别
点元与面元
- Hbase和传统数据库的区别
在说HBase之前,我想再唠叨几句.做互联网应用的哥们儿应该都清楚,互联网应用这东西,你没办法预测你的系统什么时候会被多少人访问,你面临的用户到底有多少,说不定今天你的用户还少,明天系统用户就变多了, ...
- [翻译]Component Registration in Script System 在脚本系统中注册组件
Component Registration in Script System 在脚本系统中注册组件 To refer to our component from a script, the cl ...
- ASP.NET Core2基于RabbitMQ对Web前端实现推送功能
在我们很多的Web应用中会遇到需要从后端将指定的数据或消息实时推送到前端,通常的做法是前端写个脚本定时到后端获取,或者借助WebSocket技术实现前后端实时通讯.因定时刷新的方法弊端很多(已不再采用 ...
- 饿了么 PostgreSQL 优化之旅
1. 架构演变 在O2O外卖领域,基于位置服务的需求越来越多,这就要求DB能够存储地理位置信息,而在开源数据库中,对空间地理数据支持比较好的要数PG的插件Postgis. 饿了么在使用PG的过程中,由 ...
- MSP430 G2553 LaunchPad GPIO中断
P1.P2端口上的每个管脚都支持外部中断.P1端口的所有管脚都对应同一个中断向量(Interrupt Vector),类似的,P2端口的所有管脚都对应另一个中断向量:通过PxIFG寄存器来判断中断来源 ...
- winform在A窗体刷新B窗体,并改变窗体的属性
//A窗体设置B窗体的属性并刷新B窗体 Application.OpenForm["窗体名称"].Controls["控件名称"].visible=true;
- Oracle数据库02
EXISTS子查询 特征:将主查询中的数据带到子查询中进行验证,如果验证成功则子查询返回true,当主查询接收到true的时候被验证的数据就显示,如果在子查询中验证失败则返回false,当主查询接收到 ...
- Hello world(世界,你好)
每一个学习语言的小白,第一行代码就是向世界问好,话不多,大圣要开始自己的代码人生了! 一.字符串是可以相加的,比如:print("hello"+"world") ...