源碼分析socketChannel,為什么register前需要調用selector的weakup方法


  直接上源碼,查看 register 的實現。我們查看 register 的實現會直接跟進抽象類 SelectableChannel 中:

  調用了本身的另一個 register 方法:

  該實現為抽象方法,我們直接向下查找 SelectableChannel 子類,查看其實現。因為 SocketChannel 為 ServerSocketChannel 的 accept 方法返回的,跟進 accept 方法,查看其返回了哪個實現類的實例:

  可以看到,accept 方法返回的是 SocketChannelImpl 的實例,直接跟進 SocketChannelImpl ,發現並沒有 register 方法的實現。說明 register 方法在其某個父類中已經被實現了,我們向上尋找最近的實現了 register 方法的父類,直接查看 UML 圖:

  可以看到,距離 SocketChannel 最近的持有 register 方法的父類為 AbstractSelectableChannel ,跟進去:

  findKey 為 selector 中 key 已存在的情況,key 不存在時,依靠調用 AbstractSelector 的 register 方法獲取 key。可以看出,selector.register(channel) 比較符合通常的邏輯,而這里用 channel 去 register( selector ) 本身在新獲取 key 時也是這樣實現的。暴露給我們的方法為 channel 去 register( selector ) 的原因在這里也很明顯了。channel 在設計時是可以對應多個 selector 的,也就是說同一個 channel 可以在多個 selector 上注冊自己感興趣的事件。因此每次在注冊事件時,register 方法會檢查在該 selector 上,本 channel 是否已經注冊過了,如果注冊過了,直接通過 findKey 方法返回,否則才去注冊。所以說在一個 selector 上,一個 channel 只能注冊一次。

  調用 weakup 方法的原因還沒有找到,繼續到 AbstractSelector 跟進。與上面的過程一致,發現 register 是在 SelectorImpl 中實現的:

   而 select 方法的實現基於 lockAndDoSelect 方法,他們共用了 publicKeys 這把鎖:

  在 lockAndDoSelect 方法中,用 publicKeys 鎖住了一個抽象方法 doSelect,doSelect 的實現不在解析,只需要知道這是一個阻塞方法,阻塞等待事件發生。

  所以如果沒有事件發生,鎖 publicKeys 會一直被 select 方法霸占,register 方法無法獲取鎖會一直等待鎖的釋放。所以如果在調用 register 前后不調用 weakup 方法,會發生既檢查不到死鎖,程序又卡死在 register 方法無法向下運行的情況。

  weakup 方法做的就是向事件通道寫入一個字節,將 doSelect 從阻塞中喚醒,從而繼續向下運行釋放 publicKeys 鎖,給 register 方法運行的機會。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM