Java高併發12-避免僞共享和鎖機制

一、複習

二、如何避免僞共享

  • 在JDK8之前是使用填充字節的方式來避免僞共享的,我們最終的目的其實就是希望單個變量能夠獨佔一個緩存行。
  • 我們舉一個類的例子
package com.ruigege.OtherFoundationOfConcurrent2;

public class FiledLong {
 public volatile long value =0L;
 public long p1,p2,p3,p4,p5,p6;
}

  • 可以看這個類,如果cache行僞64個字節,那麼正好能夠佔滿,七個long類型的變量,其中p1-p6都是用來佔位,還有一個對象的頭佔用八個字節,正好64個字節。
  • 在JDK8中提供了一個註解,用於避免僞共享
@sun.misc.Contended
class FiledLong2{
 public volatile long value=0L;
}
  • 用法:既可以用修飾類也可以用來修飾變量。

注意點:@Contended註解只能用於Java的核心類,比如rt包下的類,如果用戶的類需要使用這個註解的時候,需要添加JVM的參數:-XX:-RestrictContended,填充的默認寬度爲128,要自定義寬度可以設置-XX:ContendedPaddingWidth參數

三、出現僞共享內存的條件

  • 在多線程下訪問同一個緩存行的多個變量,纔會出現僞共享變量的問題,如果在單線程下訪問多個變量反而會加速訪問。

四、樂觀鎖和悲觀鎖

1.悲觀鎖

  • 定義:悲觀鎖認爲外界對數據的修改持保守態度,認爲數據很容易被外界修改,在數據被處理之前必須先加鎖,這種鎖是排他鎖,一個線程獲取了鎖之後,其他線程只能等待或者拋出異常。獲取鎖的線程,對記錄進行操作,然後提交事務釋放鎖。下面舉一個例子
 //使用悲觀鎖來獲取
 EntryObject entry = query("select * from table1 where id =#{id} for update",id);
 //修改記錄內容,根據計算修改entry記錄的屬性
 String name=generatorName(entry);
 entry.setName(name);
 
 //update操作
 int count = updateZ("update table1 set name=#{name},age=#{age} where id=#{id}",entry);
 return count;
  • 對於上面的代碼,使用了事務切面的方法,只要進入這個方法種就開始執行事物一直到這個方法結束,多個線程調用這個方法的時候,只有一個線程能夠獲取鎖,其他線程就會阻塞掛起,直到原線程釋放鎖
  • 樂觀鎖是相對悲觀鎖而存在的的方式,一般認爲如果只是訪問數據那麼就是可以不用加鎖,只有要更新數據的時候,纔會正式對數據衝突與否進行檢測,具體來說,根據update返回的行數讓用戶決定如何去做。將上面的例子改爲樂觀鎖。
package com.ruigege.OtherFoundationOfConcurrent2;

public class UpdateEntry2 {

 public int updateEntry(long id) {
  //使用樂觀鎖獲取指定記錄
  EntryObject entry = query("select * from table1 where id=#{id}",id);
  
  //
  String name = generatorName(entry);
  entry.setName(name);
  
  //update操作
  int count = update("update table1 set name=#{name},age=#{age},version=${version}+1 where id=#{id} and version =#{version}",entry);
  return count;
 }
}
  • 對比上面的代碼就可以知道根據version來進行更新數據,更新成功的話,就會給version+1,其他線程進行更新的時候,如果vesion不對的話,那麼就會停止更新。我們也是使用不斷地循環來獲取鎖而解決更新的問題
package com.ruigege.OtherFoundationOfConcurrent2;

public class updateEntry3 {

 boolean result = false;
 int retryNum = 5;
 while(retryNum>0) {
  //使用樂觀鎖獲取記錄
  EntryObject entry = query("select * from table1 where id=#{id}",id);
  String name = generatorName(entry);
  entry.setName(name);
  
  //update操作
  int count = update("update table1 set name=#{name},age=#{age},version=${version}+1 where id=#{id} and version =#{version}",entry);
  //返回的行如果不是0的話說明更新成功了,那麼即刻跳出循環
  if(count == 1) {
   result = true;
   break;
  }
  retryNum--;
  
 }
 return result;
}
  • 樂觀鎖並不會使用數據庫提供的鎖機制,一般在表中添加version字段或者使用業務狀態來實現,樂觀鎖直到提交時才鎖定,所以不會產生任何死鎖。

五、公平鎖和非公平鎖

  • 公平鎖表示線程獲取鎖的順序是按照先到先得的原則,非公平鎖在運行的時候闖入,也就是不一定先到先得。
  • ReentrantLock提供了公平和非公平鎖。

六 、源碼:

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