Java8對讀寫鎖的改進:StampedLock

        該類是一個讀寫鎖的改進,它的思想是讀寫鎖中讀不僅不阻塞讀,同時也不應該阻塞寫。

        讀不阻塞寫的實現思路:

        在讀的時候如果發生了寫,則應當重讀而不是在讀的時候直接阻塞寫

        因爲在讀線程非常多而寫線程比較少的情況下,寫線程可能發生飢餓現象,也就是因爲大量的讀線程存在並且讀線程都阻塞寫線程,

因此寫線程可能幾乎很少被調度成功!當讀執行的時候另一個線程執行了寫,則讀線程發現數據不一致則執行重讀即可。所以讀寫都存在的情況下,

使用StampedLock就可以實現一種無障礙操作,即讀寫之間不會阻塞對方,但是寫和寫之間還是阻塞的!

     

        程序舉例:

 public  class  Point {

           //一個點的xy座標

           private   double   x,y;

           /**Stamped類似一個時間戳的作用,每次寫的時候對其+1來改變被操作對象的Stamped

            * 這樣其它線程讀的時候發現目標對象的Stamped改變,則執行重讀*/

           private final   StampedLock  stampedLock   =  new    StampedLock();

  

           // an exclusively locked method

           void move(doubledeltaX,doubledeltaY) {

                   /**stampedLock調用writeLockunlockWrite時候都會導致stampedLockstamp值的變化

                  * 即每次+1,直到加到最大值,然後從0重新開始 */

                  longstamp =stampedLock.writeLock(); //寫鎖

                  try {

                         x +=deltaX;

                         y +=deltaY;

                  } finally {

                         stampedLock.unlockWrite(stamp);//釋放寫鎖

                  }

           }

  

         double distanceFromOrigin() {    // A read-only method

                 /**tryOptimisticRead是一個樂觀的讀,使用這種鎖的讀不阻塞寫

                 * 每次讀的時候得到一個當前的stamp值(類似時間戳的作用)*/

                longstamp =stampedLock.tryOptimisticRead();

     

                //這裏就是讀操作,讀取xy,因爲讀取x時,y可能被寫了新的值,所以下面需要判斷

                double    currentX =x,   currentY =y;

     

                /**如果讀取的時候發生了寫,則stampedLockstamp屬性值會變化,此時需要重讀,

                * 再重讀的時候需要加讀鎖(並且重讀時使用的應當是悲觀的讀鎖,即阻塞寫的讀鎖)

                 * 當然重讀的時候還可以使用tryOptimisticRead,此時需要結合循環了,即類似CAS方式

                 * 讀鎖又重新返回一個stampe*/

                if (!stampedLock.validate(stamp)) {

                        stamp =stampedLock.readLock(); //讀鎖

                        try {

                              currentX =x;

                              currentY =y;

                        }finally{

                              stampedLock.unlockRead(stamp);//釋放讀鎖

                       }

                }

               //讀鎖驗證成功後才執行計算,即讀的時候沒有發生寫

               return Math.sqrt(currentX *currentX + currentY *currentY);

          }

}


        StampedLock的實現思想

        在StampedLock中使用了CLH自旋鎖,如果發生了讀失敗,不立刻把讀線程掛起,鎖當中維護了一個等待線程隊列。

所有申請鎖但是沒有成功的線程都會記錄到這個隊列中,每一個節點(一個節點表示一個線程)保存一個標記位(locked),

用於判斷當前線程是否已經釋放鎖。當一個未標記到隊列中的線程試圖獲得鎖時,會取得當前等待隊列尾部的節點作爲其前序節點,

並使用類似如下代碼(一個空的死循環)判斷前序節點是否已經成功的釋放了鎖:

        while(pred.locked){  }   

        解釋:pred表示當前試圖獲取鎖的線程的前序節點,如果前序節點沒有釋放鎖,則當前線程就執行該空循環並不斷判斷前序節點的鎖釋放,

即類似一個自旋鎖的效果,避免被系統掛起。當循環一定次數後,前序節點還沒有釋放鎖,則當前線程就被掛起而不再自旋,

因爲空的死循環執行太多次比掛起更消耗資源。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章