栏目分类:
子分类:
返回
文库吧用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
文库吧 > IT > 软件开发 > 后端开发 > Java

Java运算符 ~以及nio的interestOps

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java运算符 ~以及nio的interestOps

项目场景:

背景:使用nio写服务器端接收文件的上传


问题描述

服务器端使用selector.select()来获取被关心的状态事件,使用SelectionKey的isReadable判断事件是否为可读事件,由于select()是水平触发(这里见下面的解释),所以在通道没有处理完毕之前会一直被触发。为了让触发在接受处理后就被关闭,就需要移除掉这个key的可读事件
就有了下面的代码:

@Override
	protected void readData(final SelectionKey key) throws IOException {
		//移除掉这个key的可读事件,已经在线程池里面处理
		key.interestOps(key.interestOps() & (~SelectionKey.OP_READ));//主要注意这句话
		exec.execute(new Runnable() {
			@Override
			public void run() {
				ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024);
				FileChannel fileChannel = fileMap.get(key);
				buffer.clear();
				SocketChannel socketChannel = (SocketChannel) key.channel();
				int num = 0;
				try {
					while ((num = socketChannel.read(buffer)) > 0) {
						buffer.flip();
						// 写入文件
						fileChannel.write(buffer);
						buffer.clear();
					}
				} catch (IOException e) {
					key.cancel();
					e.printStackTrace();
					return;
				}
				// 调用close为-1 到达末尾
				if (num == -1) {
					try {
						fileChannel.close();
						System.out.println("上传完毕");
						buffer.put((socketChannel.getRemoteAddress() + "上传成功").getBytes());
						buffer.clear();
						socketChannel.write(buffer);
					} catch (IOException e) {
						e.printStackTrace();
					}
					// 只有调用cancel才会真正从已选择的键的集合里面移除,否则下次select的时候又能得到
					// 一端close掉了,其对端仍然是可读的,读取得到EOF,返回-1
					key.cancel(); 
					return;
				}
				// Channel的read方法可能返回0,返回0并不一定代表读取完了。
				// 工作线程结束对通道的读取,需要再次更新键的ready集合,将感兴趣的集合重新放在里面
				key.interestOps(key.interestOps() | SelectionKey.OP_READ);
				// 调用wakeup,使得选择器上的第一个还没有返回的选择操作立即返回即重新select
				key.selector().wakeup();
			}
		});
	}

原因分析:

分析水平触发和边缘触发
(1) 水平触发是当就绪的fd未被用户进程处理,下一次select()查询依旧会返回,这是select和poll的触发方式。
(2) 边缘触发是无论就绪的fd是否被处理,下一次不再返回。理论上性能更高,但是实现相当复杂,并且任何意外的丢失事件都会造成请求处理错误。epoll默认使用水平触发,通过相应选项可以使用边缘触发。

下面来分析这句话:
key.interestOps(key.interestOps() & (~SelectionKey.OP_READ));
关键点是这个符号:~

先理解这个:

  • 正数的原码 = 反码 = 补码
  • 负数的反码 = 原码符号位不变,其它位全取反,负数的补码 = 反码 + 1。

上面话的意思是:
key.interestOps(1按照位与-2)
key.interestOps(0) //代表取消上面的四个监听,代表不监听任何东西


1、首先~表示非运算符,就是将该数的所有二进制位全取反。但又由于计算机中是以补码的形式存储的,所以0 1010全取反是1 0101(只是补码形式,还需要转成原码)。
.
2、此时得到的1 0101只是补码,我们需要将它先转为反码,反码 = 补码-1,得到反码为1 0100。
.
3、我们得到反码后,将它转为原码,原码 = 反码符号位不变,其它位全取反,得到最终的原码为1 1011,转化为十进制就是-11。


解决方案:

总结
公式:

  1. (~x) = -(x + 1)

  2. “&~xx” 代表取消xx ”|xx“ 代表将xx添加进去

转载请注明:文章转载自 www.wk8.com.cn
本文地址:https://www.wk8.com.cn/it/1040233.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 wk8.com.cn

ICP备案号:晋ICP备2021003244-6号