在一次服务器端接收客户端发送的消息时,服务器端一直触发读就绪事件,导致服务端代码出现死循环,代码如下

客户端

public class Client1 {public static void main(String[] args) throws IOException, InterruptedException {SocketChannel socketChannel = SocketChannel.open();socketChannel.configureBlocking(false);InetSocketAddress localhost = new InetSocketAddress("localhost", 8888);socketChannel.connect(localhost);while (!socketChannel.isConnected()) {socketChannel.finishConnect();}socketChannel.write(ByteBuffer.wrap("客户端数据".getBytes()));}
}

服务端

public class Server1 {public static void main(String[] args) throws IOException {ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();InetSocketAddress inetSocketAddress = new InetSocketAddress(8888);serverSocketChannel.bind(inetSocketAddress);serverSocketChannel.configureBlocking(false);Selector selector = Selector.open();serverSocketChannel.register(selector, 16);while (true) {int select = selector.select();System.out.println("当前有" + select + "个操作就绪");Set<SelectionKey> keys = selector.selectedKeys();Iterator<SelectionKey> iterator = keys.iterator();while (iterator.hasNext()) {SelectionKey next = iterator.next();if (next.isAcceptable()) {System.out.println("accept就绪++++++++++++++");ServerSocketChannel server = (ServerSocketChannel) next.channel();SocketChannel socketChannel = server.accept();socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_READ);} else if (next.isReadable()) {System.out.println("读就绪+++++++++++++++++");ByteBuffer allocate = ByteBuffer.allocate(10240000);StringBuffer stringBuffer = new StringBuffer();SocketChannel channel = (SocketChannel) next.channel();int read = channel.read(allocate);while (read > 0) {allocate.flip();stringBuffer.append(new String(allocate.array(), 0, read));allocate.clear();read = channel.read(allocate);}System.out.println(stringBuffer);}}iterator.remove();}}
}

执行结果

最后在一个帖子里看到了答案https://segmentfault.com/q/1010000019694209

原因

  • 如果客户端的socket断开连接后,会一直向服务端发送一个读就绪,即channel.read(allocate)=-1,这个时候服务端应该断开这个连接,或者至少注销掉OP_READ。

验证

  • 在客户端的代码最后加一个线程睡眠,Thread.sleep(1000000L);
    可以看到,在客户端代码执行结束之前,服务端不会收到这个读就绪事件

解决

服务器端代码加上判断

public class Server1 {public static void main(String[] args) throws IOException {ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();InetSocketAddress inetSocketAddress = new InetSocketAddress(8888);serverSocketChannel.bind(inetSocketAddress);serverSocketChannel.configureBlocking(false);Selector selector = Selector.open();serverSocketChannel.register(selector, 16);while (true) {int select = selector.select();System.out.println("当前有" + select + "个操作就绪");Set<SelectionKey> keys = selector.selectedKeys();Iterator<SelectionKey> iterator = keys.iterator();while (iterator.hasNext()) {SelectionKey next = iterator.next();if (next.isAcceptable()) {System.out.println("accept就绪++++++++++++++");ServerSocketChannel server = (ServerSocketChannel) next.channel();SocketChannel socketChannel = server.accept();socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_READ);} else if (next.isReadable()) {System.out.println("读就绪+++++++++++++++++");ByteBuffer allocate = ByteBuffer.allocate(10240000);StringBuffer stringBuffer = new StringBuffer();SocketChannel channel = (SocketChannel) next.channel();int read = channel.read(allocate);while (read > 0) {allocate.flip();stringBuffer.append(new String(allocate.array(), 0, read));allocate.clear();read = channel.read(allocate);}System.out.println(stringBuffer);// socket已经断开if (read == -1) {//增加注销操作// int readyOps = next.readyOps();//&~xx 代表取消事件,取反 按位与// next.interestOps(next.interestOps() & ~readyOps);// 或者断开服务端的这个socketchannel.close();}}}iterator.remove();}}
}

记一次JavaNIO重复读消费相关推荐

  1. 可重复读:为什么你改了我看不到呢?

    在探索问题之前,先得明白如下知识点 InnoDB 里面每个事务有一个唯一的事务 ID,叫作 transaction id.它是在事务开始的时候向 InnoDB 的事务系统申请的,是按申请顺序严格递增的 ...

  2. 数据库事务隔离级别-- 脏读、幻读、不可重复读(清晰解释)

    一.数据库事务隔离级别 数据库事务的隔离级别有4个,由低到高依次为Read uncommitted .Read committed .Repeatable read .Serializable ,这四 ...

  3. 数据库零碎要点001_数据库的4大特性(原子性_持久性_隔离性_一致性)_数据库的隔离级别(脏读_幻读_不可重复读)_mysql如何设置隔离级别

    本篇讲诉数据库中事务的四大特性(ACID),并且将会详细地说明事务的隔离级别. 如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性: ⑴ 原子性(Atomicity) 原子性是指事务 ...

  4. MySQL 乱七八糟的可重复读隔离级别实现

    点击上方"芋道源码",选择"置顶公众号" 技术文章第一时间送达! 源码精品专栏 精尽 Dubbo 原理与源码专栏( 已经完成 69+ 篇,预计总共 75+ 篇 ...

  5. MySQL的事务总结(事务特性,隔离级别,脏读,不可重复读,幻读,常见问题)

    MySQL的事务总结(事务四大特性,隔离级别,脏读,幻读) MYSQL官网:https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-iso ...

  6. mysql不可重复读和重复读_MySql隔离级别:RU / RC / RR / S + 脏读 / 不可重复读 / 幻读 / 可重复读...

    MySQL 事务 本文所说的 MySQL 事务都是指在 InnoDB 引擎下,MyISAM 引擎是不支持事务的. 数据库事务指的是一组数据操作,事务内的操作要么就是全部成功,要么就是全部失败,什么都不 ...

  7. mysql的读已提交和可重复读(Read Committed和Repeatable Read隔离级别)

    1.共享锁和排他锁 1.1.共享锁 1.2.排他锁 1.3.总述 2.MVCC 2.1.隐藏字段 2.2.Read View 2.3.Undo log 2.4.update的具体流程 2.5.可见性比 ...

  8. MySQL可重复读和读已提交实现原理,深入理解MVCC。

    1.隔离级别 MySQL中隔离级别分为4种,提未交读.读已提交.可重复读.串行化.同时MySQL默认隔离级别为可重复读. 图片 查看MySQL隔离级别 SELECT @@tx_isolation 设置 ...

  9. 快速理解脏读、不可重复读、幻读和MVCC

    理解这三种由于并发访问导致的数据读取问题,再理解事务隔离级别就简单多了. 脏读(读取未提交数据) A事务读取B事务尚未提交的数据,此时如果B事务发生错误并执行回滚操作,那么A事务读取到的数据就是脏数据 ...

最新文章

  1. java越权发送邮件_水平越权的常见解决方法
  2. 清理Mac上的软件容易吗?
  3. 在VS中建立一个易于管理的C++工程
  4. 【数据库系统概论】考研第六部分重点分析【6.1】
  5. jQuery中DOM操作方法 之 html,text,val
  6. 搜狐视频怎么更改头像
  7. JSON(5)---JSON.stringify()
  8. python生成器_Python生成器
  9. USBCNC输出板与VFD和主轴的使用
  10. 计算机怎么退出远程桌面连接,怎么退出远程桌面控制?远程桌面软件哪个好?
  11. 备战数学建模22-数学建模论文写作规范总结
  12. kettle 提交数据量_kettle大数据量读写mysql性能优化
  13. 如何解决浏览器未安装FLASH控件的提示
  14. 传奇服务端如何添加地图
  15. 泰克MDO3104示波器_六合一示波器_无限多功能
  16. 可变悬挂调节软硬_大众cc可变悬架软硬调节是什么意思
  17. 视频教程-数据库SQL查询,最佳案例讲解-SQL Server
  18. 定时任务一(quartz):纯java
  19. alexa是什么_Alexa的简要模式是什么?如何打开(或关闭)它?
  20. [小工具] chrome上日语翻译工具

热门文章

  1. fatal error: hdf5.h: No such file or directory
  2. 如何绘制caffe网络训练曲线
  3. 最渣的 Spring Boot 文章
  4. Spring Boot是什么
  5. 通过JS如何获取IP地址
  6. SpringCloud实战4-Hystrix线程隔离请求缓存请求合并
  7. UITableView 显示在statusbar 下面
  8. javascript学习总结1
  9. Magento利用input type=”file”上传图片
  10. NetBeans 时事通讯(刊号 # 60 - Jun 21, 2009)