1. 圖文并茂的聊聊ReentrantReadWriteLock的位運算

        共 677字,需瀏覽 2分鐘

         ·

        2021-11-18 09:35

        大家好,歡迎來到學(xué)習(xí)ReentrantReadWriteLock基礎(chǔ),今天我們來聊一聊讀寫狀態(tài)的設(shè)計。

        我相信不少讀者,在看JDK源碼時,會看到位運算代碼,可能有些人和阿星一樣是轉(zhuǎn)行的,缺乏計算機相關(guān)的基礎(chǔ)知識,看的是一頭霧水。

        導(dǎo)致有些人直接被勸退,也有些人選擇理解字面上的意思,細節(jié)跳過。

        但是一顆疑惑的種子在我們心中埋了下來「為什么使用位運算就能達到這樣的效果?」。

        恰好ReentrantReadWriteLock讀寫狀態(tài)的設(shè)計用到了位運算,我們以此來展開今天的話題。

        一段位運算代碼

        我們來到ReentrantReadWriteLock.Sync內(nèi)部類,發(fā)現(xiàn)了這段代碼(后面以RRW簡稱

        ????????
        //偏移位數(shù)
        static?final?int?SHARED_SHIFT?=?16;
        //讀鎖計數(shù)基本單位
        static?final?int?SHARED_UNIT?=?(1?<//讀鎖、寫鎖可重入最大數(shù)量
        static?final?int?MAX_COUNT?=?(1?<1;
        //獲取低16位的條件
        static?final?int?EXCLUSIVE_MASK?=?(1?<1;

        //獲取讀鎖重入數(shù)
        static?int?sharedCount(int?c)????{?return?c?>>>?SHARED_SHIFT;?}
        //獲取寫鎖重入數(shù)
        static?int?exclusiveCount(int?c)?{?return?c?&?EXCLUSIVE_MASK;?}

        上面的這些位運算代碼是用來干嘛的?

        因為RRW的中的int整型變量state要同時維護讀鎖、寫鎖兩種狀態(tài),所以RRW的是通過高低位切割來實現(xiàn)。

        int4個字節(jié),一個字節(jié)8位,總共32位,切割一下,高16位表示讀,低16位表示寫。

        這樣做的好處就是節(jié)約資源,就像現(xiàn)實中老板把你一個人當(dāng)兩個人用是一樣的道理。

        講到這里,大家也明白了,上面的位運算代碼就是完成高低位切割的。

        讀鎖位運算

        //偏移位數(shù)
        static?final?int?SHARED_SHIFT?=?16;
        //讀鎖計數(shù)基本單位
        static?final?int?SHARED_UNIT?=?(1?<

        讀鎖使用高16位,每次獲取讀鎖成功+1,所以讀鎖計數(shù)基本單位是1的高16位,即1左移16位(1 << 16)。

        1左移16位等于65536,每次獲取讀鎖成功都+65536,這時有讀者跳出來問,不是+1嘛,怎么變成+65536了,這不對啊。

        別急別急,看看下面這段代碼

        //偏移位數(shù)
        static?final?int?SHARED_SHIFT?=?16;
        //獲取讀鎖重入數(shù)
        static?int?sharedCount(int?c)????{?return?c?>>>?SHARED_SHIFT;?}

        上面sharedCount函數(shù)通過位運算是做無符號右移16位獲取讀鎖的重入數(shù),為什么可以獲取到呢?

        阿星原地向前走16步,再后退16步,又回到原點,1左移16位等于65536,65536右移16位等于1

        比如我們獲取到了3次讀鎖,就是65536 * 3 = 196608,轉(zhuǎn)換下公式就是3左移16位等于196608,196608右移16位等于3

        雖然我們每次獲取到讀鎖都會+65536,但是獲取讀鎖時會做右移16位,所以效果和+1是一樣。

        寫鎖位運算

        //偏移位數(shù)
        static?final?int?SHARED_SHIFT?=?16;
        //獲取低16位的條件
        static?final?int?EXCLUSIVE_MASK?=?(1?<1;
        //獲取寫鎖重入數(shù)
        static?int?exclusiveCount(int?c)?{?return?c?&?EXCLUSIVE_MASK;?}

        剩下的寫鎖就非常簡單,獲取低16位不用左右移動,只要把高16位全部補0即可。

        反推一下,因為不需要左右移動,其實就和正常的數(shù)字一樣,只不過因為高16位補0,導(dǎo)致數(shù)值范圍在0~65535,也就是說寫鎖獲取成功直接+1就好了。

        我們目光轉(zhuǎn)到EXCLUSIVE_MASK變量,1右移16位后-1,得到6553565535的二進制就是111111111111111。

        現(xiàn)在來看exclusiveCount函數(shù),該函數(shù)內(nèi)做了位運算&,&又稱""運算。

        ""運算是兩個二進制,每位數(shù)運算,運算規(guī)則如下

        • 0&0=0
        • 0&1=0
        • 1&0=0
        • 1&1=1

        如果相對應(yīng)位都是1,則結(jié)果為1,否則為0

        可能有些讀者大大還是不太明白,下面放張圖16位二進制""運算圖

        我們發(fā)現(xiàn)""運算時,只要有一方為0,那結(jié)果一定是0,所以為了切割低16位,可以使用&來完成。

        從上圖可以看出,EXCLUSIVE_MASK16位都是0,低16位都是1,和它&的變量,高16位全部會變成0,低16位全部保留下來,最終達到獲取低16位效果。

        c & EXCLUSIVE_MASK,假設(shè)c1,&的過程如下圖

        這樣看可能沒太大感覺,我們把數(shù)值調(diào)大點,假設(shè)c6553665537,&的過程如下圖

        現(xiàn)在有感覺了吧,c的高16位都會變成0,低16位會原樣保留,最終達到獲取低16位效果。

        EXCLUSIVE_MASK范圍在0~65535,所以c的范圍也不會超過0~65535,因為超過了也會通過& EXCLUSIVE_MASK回到0~65535。

        提個問題

        阿星」:int如何實現(xiàn)序列化與反序列化?

        萌新」:好家伙,我直接用Integer就好了,父類Number實現(xiàn)了序列化接口Serializable。

        阿星」:不使用Serializable,自己手寫一個呢?

        萌新」:啊,這。。。。

        為了讓大家更好的消化之前的內(nèi)容,阿星手把手帶大家實現(xiàn)int與字節(jié)的互轉(zhuǎn)。

        int4個字節(jié),一個字節(jié)8位,總共32位。

        int轉(zhuǎn)byte數(shù)組

        思路很簡單,我們只需要從右往左按8位一個一個截取,再存儲到byte數(shù)組里面,代碼如下:

        public?static?byte[]?intToBytes(int?n)?{
        ????????//長度4字節(jié)的數(shù)組
        ????????byte[]?buf?=?new?byte[4];
        ????????for?(int?i?=?0;?i?????????????//循環(huán)右移動8位,存儲到數(shù)組中
        ????????????buf[i]?=?(byte)?(n?>>?(8?*?i));
        ????????}
        ????????return?buf;
        ????}

        過程圖如下

        byte數(shù)組轉(zhuǎn)int

        我們從int轉(zhuǎn)換成了byte[],現(xiàn)在要從byte[]轉(zhuǎn)換成int,代碼如下

        public?static?int?bytesToInt(byte[]?buf)?{
        ????return?buf[0]?&?0xff
        ????????????|?((buf[1]?<8)?&?0xff00)
        ????????????|?((buf[2]?<16)?&?0xff0000)
        ????????????|?((buf[3]?<24)?&?0xff000000);
        }

        代碼中涉及到了"左移、與、或"位運算,左移我們前面都說了,還有一個,一樣,只是運算規(guī)則不同,的運算規(guī)則如下

        • 0&0=0
        • 0&1=1
        • 1&0=1
        • 1&1=1

        如果相對應(yīng)位都是 0,則結(jié)果為 0,否則為 1

        0xff的二進制是11111111,0xff后面每追加20,效果等于左移8位,依次類推,所以我們最終是利用<<、|、&、0xff來還原。

        過程圖如下

        小結(jié)

        ReentrantReadWriteLock中讀寫狀態(tài)公用一個狀態(tài),巧妙的利用高低位來節(jié)約資源,在整個實現(xiàn)過程中,使用了位運算來做高低位切割。

        歷史好文推薦

        瀏覽 52
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
          
          

            1. 久久久久久久久久一区二区三区 | 波多野结衣的三级电影 | 艹逼逼| 天天肉天天干夜夜干天天干夜夜干 | 色黄网站 |