netty之wakeup详解
有關wakeup變量的使用有一下幾個地方(這里只使用netty普通任務舉例,不討論定時任務)
部分1,NioEventLoop.select()
if (hasTasks() && wakenUp.compareAndSet(false, true)) {selector.selectNow();selectCnt = 1;break;}int selectedKeys = selector.select(timeoutMillis);selectCnt ++;if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) {break;}部分2 NioEventLoop.run()
select(wakenUp.getAndSet(false));if (wakenUp.get()) {selector.wakeup();}部分3 NioEventLoop.wakeup()
protected void wakeup(boolean inEventLoop) {if (!inEventLoop && wakenUp.compareAndSet(false, true)) {selector.wakeup();}}1,2是reactor線程進行操作的方法 3是用戶線程操作的方法
方法三比較好理解,就是在添加完一個任務后,去修改這個wakeup變量,然后去喚醒這個reactor線程
首先關注2,值得注意的是getAndSet返回的是修改之前的值,但是在這個select(wakenUp.getAndSet(false))之后又調用了一次selector.wakeup(); 為什么要這樣做呢???
舉例:
情況一
wakeup此時為默認值,但是現在用戶線程在select(wakenUp.getAndSet(false)) 之前 修改wakeup為 true,調用了 wakeup()
此時傳入select(wakenUp.getAndSet(false))的參數oldwakeup為 true ,wake的最新值,筆者稱為newwakeup 這里為 false
因為用戶線程調用了wakeup(),所以阻塞的select 方法不會阻塞
所以成功的沒有進過阻塞進入break 因為沒有阻塞,這種情況我稱為喚醒成功
情況二
但是現在用戶線程在select(wakenUp.getAndSet(false)) 之后 修改wakeup會失敗,wakeup()不會調用
select(wakenUp.getAndSet(false))的參數oldwakeup為 false,newwakeup 為true
在部分1中if (hasTasks() && wakenUp.compareAndSet(false, true)) 這個判斷會進入,然后調用了一次selectNow(),然后就break了
沒有阻塞,喚醒成功
以上兩中情況,針對的是第一次遍歷執行
下面進入第二次遍歷執行
當上面的情況被喚醒后,就會來到 if (wakenUp.get()) 這個判斷,這里如果是被喚醒過來的,這里就會為true,又調用了一次wakeup
那么當再次進入oldwakeup為true,newwakeup為false,所以又重復了第一次遍歷中的第一種情況,那么為什么要這么做???
加入netty在第二次遍歷的時候又進來的一個任務呢?此時是沒有辦法調用到wakeup()這個方法的,因為調用這個方法之前有一個判斷
wakenUp.compareAndSet(false, true),這個判斷不會通過,而部分2中的wakeup()就是解決這個情況的
對于后面的遍歷,如果沒有設計到喚醒操作,那么oldwakeup 和 newwakeup都是false,netty就會進入正常的輪詢了
那么現在又有一個問題,為什么需要使用到wakeup這個變量?
通過這個變量的類型可以知道 wakeup 是 AtomicBoolean,所以一定是為了解決并發問題的,如果說多個線程都需要喚醒操作本來是需要執行多次wakeup()這個操作的,但是wakeup()是個耗時的操作,所以使用到AtomicBoolean的compareAndSet(expect, update)方法,使得多次wakeup()都合并成一個wakeup()
總結
以上是生活随笔為你收集整理的netty之wakeup详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SCT1270,SCT1271,12.6
- 下一篇: 第四章 大数据之hive搭建