<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          為什么要使用位運(yùn)算表示線程池狀態(tài)?

          共 7926字,需瀏覽 16分鐘

           ·

          2021-03-07 04:30


          0 文章概述

          我們閱讀ThreadPoolExecutor源碼時在開篇就會發(fā)現(xiàn)很多位運(yùn)算代碼:

          public?class?ThreadPoolExecutor?extends?AbstractExecutorService?{
          ????private?final?AtomicInteger?ctl?=?new?AtomicInteger(ctlOf(RUNNING,?0));
          ????private?static?final?int?COUNT_BITS?=?Integer.SIZE?-?3;
          ????private?static?final?int?CAPACITY???=?(1?<<?COUNT_BITS)?-?1;

          ????private?static?final?int?RUNNING????=?-1?<<?COUNT_BITS;
          ????private?static?final?int?SHUTDOWN???=??0?<<?COUNT_BITS;
          ????private?static?final?int?STOP???????=??1?<<?COUNT_BITS;
          ????private?static?final?int?TIDYING????=??2?<<?COUNT_BITS;
          ????private?static?final?int?TERMINATED?=??3?<<?COUNT_BITS;

          ????private?static?int?runStateOf(int?c)?????{?return?c?&?~CAPACITY;?}
          ????private?static?int?workerCountOf(int?c)??{?return?c?&?CAPACITY;?}
          ????private?static?int?ctlOf(int?rs,?int?wc)?{?return?rs?|?wc;?}
          }

          不難發(fā)現(xiàn)線程狀態(tài)都用位運(yùn)算表示,但是為什么要這樣做呢?為什么不定義為直觀的數(shù)字呢?下面我們進(jìn)行分析。雖然代碼量不多,但是想要理解線程池就必須要理解為什么使用位運(yùn)算。

          ThreadPoolExecutor在設(shè)計(jì)時就是用一個int數(shù)值表示了兩個業(yè)務(wù)含義:線程池狀態(tài)和線程數(shù)量。其中高3位表示線程池狀態(tài),低29位表示線程數(shù)量,這個設(shè)計(jì)思想體現(xiàn)在以下三句代碼:

          //?代碼1
          private?final?AtomicInteger?ctl?=?new?AtomicInteger(ctlOf(RUNNING,?0));
          //?代碼2
          private?static?final?int?COUNT_BITS?=?Integer.SIZE?-?3;
          //?代碼3
          private?static?final?int?CAPACITY???=?(1?<<?COUNT_BITS)?-?1;

          代碼1表示用一個int保存線程池信息,代碼2表示一共有(32-3)=29位可以表示線程數(shù)量,代碼3表示理論上最大線程數(shù)量為536870911,這個理論值足以支撐線程池使用。


          1 線程池狀態(tài)

          我們明白了線程池上述設(shè)計(jì)思想,下面就來分析線程池狀態(tài)值:

          private?static?final?int?RUNNING????=?-1?<<?COUNT_BITS;
          private?static?final?int?SHUTDOWN???=??0?<<?COUNT_BITS;
          private?static?final?int?STOP???????=??1?<<?COUNT_BITS;
          private?static?final?int?TIDYING????=??2?<<?COUNT_BITS;
          private?static?final?int?TERMINATED?=??3?<<?COUNT_BITS;

          我們知道COUNT_BITS=29則上述代碼等價(jià)于:

          private?static?final?int?RUNNING????=?-1?<<?29;
          private?static?final?int?SHUTDOWN???=??0?<<?29;
          private?static?final?int?STOP???????=??1?<<?29;
          private?static?final?int?TIDYING????=??2?<<?29;
          private?static?final?int?TERMINATED?=??3?<<?29;

          現(xiàn)在我們算一算這些狀態(tài)等于多少,在這里我們從后往前算,因?yàn)镽UNNING狀態(tài)是負(fù)數(shù)左移運(yùn)算,計(jì)算步驟稍微多一些。

          (1) TERMINATED = 3 << 29

          初始值:00000000?00000000?00000000?00000011
          左移后:01100000?00000000?00000000?00000000

          (2) TIDYING = 2 << 29

          初始值:00000000?00000000?00000000?00000010
          左移后:01000000?00000000?00000000?00000000

          (3) STOP = 1 << 29

          初始值:00000000?00000000?00000000?00000001
          左移后:00100000?00000000?00000000?00000000

          (4) SHUTDOWN = 0 << 29

          初始值:00000000?00000000?00000000?00000000
          左移后:00000000?00000000?00000000?00000000

          (5) RUNNING = -1 << 29

          原碼:10000000?00000000?00000000?00000001
          反碼:11111111?11111111?11111111?11111110(原碼符號位不變、數(shù)值位取反)
          補(bǔ)碼:11111111?11111111?11111111?11111111(反碼+1)
          左移:11100000?00000000?00000000?00000000

          2 位運(yùn)算應(yīng)用

          runStateOf方法用來獲取線程池狀態(tài)信息,workerCountOf方法用來獲取線程池線程數(shù),ctlOf方法用來設(shè)置當(dāng)前線程池狀態(tài)和線程數(shù)量信息,我們分別進(jìn)行計(jì)算。

          private?static?int?runStateOf(int?c)?????{?return?c?&?~CAPACITY;?}
          private?static?int?workerCountOf(int?c)??{?return?c?&?CAPACITY;?}
          private?static?int?ctlOf(int?rs,?int?wc)?{?return?rs?|?wc;?}

          (1) CAPACITY ? = (1 << 29) - 1

          先左移:00100000?00000000?00000000?00000000
          再減一:00011111?11111111?11111111?11111111

          (2) ~CAPACITY

          原始值:00011111?11111111?11111111?11111111
          取反后:11100000?00000000?00000000?00000000

          (3) runStateOf

          現(xiàn)在一個線程池狀態(tài)是RUNNING并且線程數(shù)量等于3用二進(jìn)制表示如下:

          11100000?00000000?00000000?00000011

          執(zhí)行runStateOf方法就可以得到線程池狀態(tài):

          11100000?00000000?00000000?00000011
          &
          11100000?00000000?00000000?00000000
          =
          11100000?00000000?00000000?00000000

          (4) workerCountOf

          現(xiàn)在一個線程池狀態(tài)是RUNNING并且線程數(shù)量等于4用二進(jìn)制表示如下:

          11100000?00000000?00000000?00000100

          執(zhí)行workerCountOf方法就可以得到線程數(shù)量:

          11100000?00000000?00000000?00000100
          &
          00011111?11111111?11111111?11111111
          =
          00000000?00000000?00000000?00000100

          (5) ctlOf

          現(xiàn)在我們要設(shè)置一個狀態(tài)是RUNNING且線程數(shù)量等于4的線程池ctl值:

          private?final?AtomicInteger?ctl?=?new?AtomicInteger(ctlOf(RUNNING,?4));

          11100000?00000000?00000000?00000000
          |
          00000000?00000000?00000000?00000100
          =
          11100000?00000000?00000000?00000100

          這個方法真正體現(xiàn)了高3位表示線程池狀態(tài),低29位表示線程數(shù)量這個設(shè)計(jì)思想優(yōu)點(diǎn),原本需要兩步設(shè)置動作現(xiàn)在只需要一步,從而實(shí)現(xiàn)了操作原子性,這樣就可以滿足線程池的很多CAS操作,例如線程池在調(diào)用addWorker新增工作線程數(shù)時會調(diào)用compareAndIncrementWorkerCount方法增加線程數(shù)量。

          但是假設(shè)同一時刻shutdownNow方法導(dǎo)致線程池狀態(tài)發(fā)生改變,那么新增工作線程數(shù)方法就不會調(diào)用成功,需要繼續(xù)執(zhí)行自旋進(jìn)行嘗試,這體現(xiàn)了線程狀態(tài)和線程數(shù)量維護(hù)的原子性。


          3 位圖法應(yīng)用

          3.1 需求背景

          我們看看位運(yùn)算怎樣應(yīng)用在實(shí)際開發(fā)場景。假設(shè)在系統(tǒng)中用戶一共有三種角色:普通用戶、管理員、超級管理員,現(xiàn)在需要設(shè)計(jì)一張用戶角色表記錄這類信息。我們不難設(shè)計(jì)出如下方案:

          idnamesuperadminnormal
          101用戶一100
          102用戶二010
          103用戶三001
          104用戶四111

          我們使用1表示是,0表示否,那么觀察上表不難得出,用戶一有用超級管理員角色,用戶二具有管理員角色,用戶三具有普通用戶角色,用戶四同時具有三種角色。如果此時新增加一種角色呢?那么新增一個字段即可。


          3.2 發(fā)現(xiàn)問題

          按照上述一個字段表示一種角色進(jìn)行表設(shè)計(jì)功能上是沒有問題的,優(yōu)點(diǎn)是容易理解結(jié)構(gòu)清晰,但是我們想一想有沒有什么問題?筆者遇到過如下問題:在復(fù)雜業(yè)務(wù)環(huán)境一份數(shù)據(jù)可能會使用在不同的場景,例如上述數(shù)據(jù)存儲在MySQL數(shù)據(jù)庫,這一份數(shù)據(jù)還會被用在如下場景:

          檢索數(shù)據(jù)需要同步一份到ES

          業(yè)務(wù)方使用此表通過Flink計(jì)算業(yè)務(wù)指標(biāo)

          業(yè)務(wù)方訂閱此表Binlog消息進(jìn)行業(yè)務(wù)處理

          如果表結(jié)構(gòu)發(fā)生變化,數(shù)據(jù)源之間就要重新進(jìn)行對接,業(yè)務(wù)方也要進(jìn)行代碼修改,這樣開發(fā)成本比較非常高。有沒有辦法避免此類問題?


          3.3 解決方案

          我們可以使用位圖法,這樣同一個字段可以表示多個業(yè)務(wù)含義。首先設(shè)計(jì)如下數(shù)據(jù)表,userFlag字段暫時不填。

          idnameuser_flag
          101用戶一暫時不填
          102用戶二暫時不填
          103用戶三暫時不填
          104用戶四暫時不填


          我們設(shè)計(jì)位圖每一個bit表示一種角色:

          bf3a09e3526a5172fa17dd6134210cb1.webp


          我們使用位圖法表示如下數(shù)據(jù)表:

          idnamesuperadminnormal
          101用戶一100
          102用戶二010
          103用戶三001
          104用戶四111


          用戶一位圖如下十進(jìn)制數(shù)值等于4:

          7e8699d0f4b9ce84472fafc9352170b3.webp


          用戶二位圖如下十進(jìn)制數(shù)值等于2:

          6e39dcb86e7cf5ba7afe142b3dcc6065.webp


          用戶三位圖如下十進(jìn)制數(shù)值等于1:

          a7a2550c2caefd0cc8450ec0b2764e92.webp


          用戶四位圖如下十進(jìn)制數(shù)值等于7:

          155290a2057be5eacc953fbc1ac7dad3.webp


          現(xiàn)在我們可以填寫數(shù)據(jù)表第三列:

          idnameuser_flag
          101用戶一4
          102用戶二2
          103用戶三1
          104用戶四7

          3.4 代碼實(shí)例

          (1) 枚舉定義

          定義枚舉時不要直接定義為1、2、4這類數(shù)字,而是采用位移方式進(jìn)行定義,這樣使用者可以明白設(shè)計(jì)者的意圖。

          /**
          ?*?用戶角色枚舉
          ?*
          ?*?@author?微信公眾號「JAVA前線」
          ?*
          ?*/

          public?enum?UserRoleEnum?{

          ????//?1?->?00000001
          ????NORMAL(1,?"普通用戶"),

          ????//?2?->?00000010
          ????MANAGER(1?<<?1,?"管理員"),

          ????//?4?->?00000100
          ????SUPER(1?<<?2,?"超級管理員")

          ????;

          ????private?int?code;
          ????private?String?description;

          ????private?UserRoleEnum(Integer?code,?String?description)?{
          ????????this.code?=?code;
          ????????this.description?=?description;
          ????}

          ????public?String?getDescription()?{
          ????????return?description;
          ????}

          ????public?int?getCode()?{
          ????????return?this.code;
          ????}
          }

          假設(shè)用戶已經(jīng)具有普通用戶角色,我們需要為其增加管理員角色,這就是新增角色,與之對應(yīng)還有刪除角色和查詢角色,這些操作需要用到為位運(yùn)算,詳見代碼注釋。

          /**
          ?*?用戶角色枚舉
          ?*
          ?*?@author?微信公眾號「JAVA前線」
          ?*
          ?*/

          public?enum?UserRoleEnum?{

          ????//?1?->?00000001
          ????NORMAL(1,?"普通用戶"),

          ????//?2?->?00000010
          ????MANAGER(1?<<?1,?"管理員"),

          ????//?4?->?00000100
          ????SUPER(1?<<?2,?"超級管理員")

          ????;

          ????//?新增角色?->?位或操作
          ????//?oldRole?->?00000001?->?普通用戶
          ????//?addRole?->?00000010?->?新增管理員
          ????//?newRole?->?00000011?->?普通用戶和管理員
          ????public?static?Integer?addRole(Integer?oldRole,?Integer?addRole)?{
          ????????return?oldRole?|?addRole;
          ????}

          ????//?刪除角色?->?位異或操作
          ????//?oldRole?->?00000011?->?普通用戶和管理員
          ????//?delRole?->?00000010?->?刪除管理員
          ????//?newRole?->?00000001?->?普通用戶
          ????public?static?Integer?removeRole(Integer?oldRole,?Integer?delRole)?{
          ????????return?oldRole?^?delRole;
          ????}

          ????//?是否有某種角色?->?位與操作
          ????//?allRole?->?00000011?->?普通用戶和管理員
          ????//?qryRole?->?00000001?->?是否有管理員角色
          ????//?resRole?->?00000001?->?有普通用戶角色
          ????public?static?boolean?hasRole(Integer?allRole,?Integer?qryRole)?{
          ????????return?qryRole?==?(role?&?qryRole);
          ????}

          ????private?int?code;
          ????private?String?description;

          ????private?UserRoleEnum(Integer?code,?String?description)?{
          ????????this.code?=?code;
          ????????this.description?=?description;
          ????}

          ????public?String?getDescription()?{
          ????????return?description;
          ????}

          ????public?int?getCode()?{
          ????????return?this.code;
          ????}

          ????public?static?void?main(String[]?args)?{
          ????????System.out.println(addRole(1,?2));
          ????????System.out.println(removeRole(3,?1));
          ????????System.out.println(hasRole(3,?1));
          ????}
          }

          (2) 數(shù)據(jù)查詢

          假設(shè)在運(yùn)營后臺查詢界面中,需要查詢具有普通用戶角色的用戶數(shù)據(jù),可以使用如下SQL語句:

          select?*?from?user_role?where?(user_flag?&?1)?=?user_flag;
          select?*?from?user_role?where?(user_flag?&?b'0001')?=?user_flag;

          我們也可以使用如下MyBatis語句:

          <select?id="selectByUserRole"?resultMap="BaseResultMap"?parameterType="java.util.Map">
          ??select?*?from?user_role?
          ??where?user_flag?&?#{userFlag}?=?#{userFlag}
          </select>

          <select?id="selectByUserIdAndRole"?resultMap="BaseResultMap"?parameterType="java.util.Map">
          ??select?*?from?user_role?
          ??where?id?=?#{userId}?and?user_flag?&?#{userFlag}?=?#{userFlag}
          </select>

          4 文章總結(jié)

          本文首先分析了位運(yùn)算在Java線程池源碼的應(yīng)用,然后我們又介紹了位圖法,這樣一個字段就可以表示多個含義,從而減少了字段冗余,節(jié)省了對接和開發(fā)的成本。當(dāng)然位圖法也有缺點(diǎn),例如數(shù)據(jù)庫字段含義不直觀需要進(jìn)行轉(zhuǎn)義,增加了代碼理解成本,大家可以根據(jù)需求場景選擇使用,希望本文對大家有所幫助。



          --? End? --

          ———————


          1.原創(chuàng)不易,你的在看是我創(chuàng)作的動力。

          2.歡迎關(guān)注公眾號?牧小碼農(nóng)「帶你一起學(xué)Java」

          3.疫情期間,勤洗手,戴口罩,做好個人防護(hù)。



          ??????????

          ????????????????????

          “在看轉(zhuǎn)發(fā)”是最大的支持

          瀏覽 44
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  内射学生妹J亅 | 欧美精品一一色哟哟 | 成人免费黄色A片 | 91日韩在线视频 | 额去撸在线视频 |