public boolean translateAndUpdateReadyOps(int var1, SelectionKeyImpl var2) {
return this.translateReadyOps(var1, var2.nioReadyOps(), var2);
}
public boolean translateAndSetReadyOps(int var1, SelectionKeyImpl var2) {
return this.translateReadyOps(var1, 0, var2);
}
通过观察两个方法,可以发现它们都调用了translateReadyOps方法:
/**
-
@param var1 该通道发生的事件类型,为Net中的POLL类型属性,这里需注意的有3个POLL类型属性:POLLIN、
-
POLLOUT、POLLCONN,分别对应SelectionKey的OP_READ、OP_WRITE、OP_CONNECT
-
@param var2 translateAndUpdateReadyOps中该参数为var3的readyOps,在translateAndSetReadyOps * 中该参数为0
-
@param var3 需修改的SelectionKey
*/
public boolean translateReadyOps(int var1, int var2, SelectionKeyImpl var3) {
int var4 = var3.nioInterestOps();
int var5 = var3.nioReadyOps();
int var6 = var2;
if ((var1 & Net.POLLNVAL) != 0) {
return false;
} else if ((var1 & (Net.POLLERR | Net.POLLHUP)) != 0) {
var3.nioReadyOps(var4);
this.readyToConnect = true;
return (var4 & ~var5) != 0;
} else {
//var1 & var2 !=0 类似于 var1 == var2,或var1=0或var2=0
//判断发生的事件是否是Net.POLLIN,如果是则判断该通道对应的SelectionKey的readyOps是否等于1(OP_READ的值)
if ((var1 & Net.POLLIN) != 0 && (var4 & 1) != 0 && this.state == 2) {
var6 = var2 | 1;//等同于:var2 | OP_READ
}
//判断发生的事件是否是Net.POLLCONN,如果是则判断该通道对应的SelectionKey的readyOps是否等于8(OP_CONNECT的值)
if ((var1 & Net.POLLCONN) != 0 && (var4 & 8) != 0 && (this.state == 0 || this.state == 1)) {
var6 |= 8;//等同于:var6 | OP_CONNECT
this.readyToConnect = true;
}
//判断发生的事件是否是Net.POLLOUT,如果是则这判断该通道对应的SelectionKey的readyOps是否等于4(OP_OP_WRITE的值)
if ((var1 & Net.POLLOUT) != 0 && (var4 & 4) != 0 && this.state == 2) {
var6 |= 4;//等同于:var6 | OP_READ
}
var3.nioReadyOps(var6);
return (var6 & ~var5) != 0;
}
}
看完上面源码可以发现,在判断通道实际发生事件的POLL类型时,还需判断该POLL类型对应的SelectionKey的ops类型是否与该SelectionKey的关注键interestOps相同,当所有条件满足时再将var6与其ops类型按位或,最后将该SelectionKey即var3的预备键readyOps设为var6。总结起来就是:
当通道实际发生的操作事件类型不等于该通道对应的SelectionKey的关注键interestOps时, 预备键readyOps不会被修改,且该发生事件的通道对应的SelectionKey也不会被加入Selector的selectedKeys集合中(从下面代码中可看出),但readyOps不一定就等于interestOps,因为调用interestOps的修改或设置方法时并不会同时修改readyOps。为了防止在进行select操作时,有另一个线程修改了某一SelectionKey的interestOps属性,在interestOps前添加了volatile修饰符,保证其可见性。
//这里是processFDSet方法中的一段代码,var10为SelectionKey
/*可以看出在进行了translateAndSetReadyOps或translateAndUpdateReadyOps方法设置readyOps属性后,
- 为了防止这时有另一线程修改了interestOps或readyOps为0,还判断了SelectionKey的readyOps和interestOps是否相同,
*/相同才把该SelectionKey加入到选择器的selectedKeys属性中
if (var9.clearedCount != var1) {
var10.channel.translateAndSetReadyOps(var4, var10);
if ((var10.nioReadyOps() & var10.nioInterestOps()) != 0) {
WindowsSelectorImpl.this.selectedKeys.add(var10);
var9.updateCount = var1;
++var6;
}
} else {
var10.channel.translateAndUpdateReadyOps(var4, var10);
if ((var10.nioReadyOps() & var10.nioInterestOps()) != 0) {
WindowsSelectorImpl.this.selectedKeys.add(var10);
var9.updateCount = var1;
++var6;
}
}
SelectionKeyImpl的方法
===================
在SelectionKeyImpl中除开实现了SelectionKey抽象方法的简单方法外,需要关注几个较为重要的方法:
//确保该SelectionKey是有效的,如无效则会之间抛出异常
//在几个关于SelectionKey的属性方法中都有调用,如interestOps() 、interestOps(int var1)、readyOps()
private void ensureValid() {
if (!this.isValid()) {
throw new CancelledKeyException();
}
}
//设置readyOps的方法,不会确保该SelectionKey的有效性
public void nioReadyOps(int var1) {
this.readyOps = var1;
}
//该方法是SelectionKeyImpl自定义的获取readyOps方法,不同于readyOps(),它不会确保SelectionKey的有效性
public int nioReadyOps() {
return this.readyOps;
}
//获取interestOps的方法,interestOps()会通过调用该方法进行获取,是最终实现获取nterestOps的方法
public int nioInterestOps() {
return this.interestOps;
}
在SelectionKeyImpl类里,设置interestOps相比设置readyOps较为复杂,因为他调用了两个通道的方法validOps()和translateAndSetInterestOps(int var1, SelectionKeyImpl var2):
//interestOps(int var1)中调用了该方法来设置interestOps,是最终实现设置interestOps的方法
public SelectionKey nioInterestOps(int var1) {
if ((var1 & ~this.channel().validOps()) != 0) {
throw new IllegalArgumentException();
} else {
this.channel.translateAndSetInterestOps(var1, this);
this.interestOps = var1;
return this;
}
}
首先先来看看nioInterestOps方法中判断语句内的代码块:
(var1 & ~this.channel().validOps()) != 0
这里的this.channel有5个可能的实现类:SeverSocketChannel、SocketChannel、SinkChannel、SourceChannel、DatagramChannel,因为不同的通道可能只会发生对应的操作事件,如SeverSocketChannel只会发生OP_ACCEPT操作,SocketChannel会发生OP_READ、OP_WRITE和OP_CONNECT,而validOps()就是返回通道对应可能发生的操作事件(有效操作事件):
//SeverSocketChannel
public final int validOps() {
return SelectionKey.OP_ACCEPT;
}
//SocketChannel
public final int validOps() {
return (SelectionKey.OP_READ
| SelectionKey.OP_WRITE
| SelectionKey.OP_CONNECT);
}
所以nioInterestOps方法的判断语句可以改为:
(var1 & ~有效的操作事件) != 0
而后又对有效的操作事件进行了取非,变成了:
(var1 & 无效的操作事件) != 0
在之前说过 var1 & var2 !=0 类似于 var1 == var2,所以当判断语句内的条件成立时,即说明设置的输入的参数不在该SelectionKey对应的通道的有效操作事件里,所以抛出 IllegalArgumentException() 异常。
当参数var1为该SelectionKey对应的通道的有效操作事件时,在调用通道的translateAndSetInterestOps方法将该关注类型对应的POLL类型写入到选择器的pollWrapper属性中:
//SocketChannel
public void translateAndSet InterestOps(int var1, SelectionKeyImpl var2) {
int var3 = 0;
//OP_READ
if ((var1 & 1) != 0) {
var3 |= Net.POLLIN;
}
//OP_WRITE
if ((var1 & 4) != 0) {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
分享
这次面试我也做了一些总结,确实还有很多要学的东西。相关面试题也做了整理,可以分享给大家,了解一下面试真题,想进大厂的或者想跳槽的小伙伴不妨好好利用时间来学习。学习的脚步一定不能停止!
Spring Cloud实战
Spring Boot实战
面试题整理(性能优化+微服务+并发编程+开源框架+分布式)
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
的小伙伴不妨好好利用时间来学习。学习的脚步一定不能停止!
[外链图片转存中…(img-CNKhZojo-1712661094318)]
Spring Cloud实战
[外链图片转存中…(img-g4DjGOOH-1712661094319)]
Spring Boot实战
[外链图片转存中…(img-UwZcNpdX-1712661094319)]
面试题整理(性能优化+微服务+并发编程+开源框架+分布式)
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-DAKHHltK-1712661094319)]