【連載】第2章-2.5活躍性與性能(緩存未必帶來性能提升) 緩存未必帶來性能提升 減小鎖粒度 合理的同步代碼塊大小 耗時長的代碼塊不要使用鎖

格言:在程序猿界混出點名堂!

已經連載了2個周,本書的第2章的解讀也要結束了,下週可以開始第3章的解讀,說實話,雖然很累,但是每天保持能夠堅持讀一個章節,慢慢尋味和體會,然後進行組織與總結,把書中最好的東西分享給大家。也是一種快樂。帶給快樂的不僅是自己還有你們。另外需要看前面幾期的解讀,可以添加文章最下面的微信公衆號。

《JAVA併發編程實戰》解讀
【連載】第2章-2.5活躍性與性能

回顧:上一節主要介紹了用鎖來構造一種協議來保護共享狀態的訪問,也介紹了將對象的可變狀態封裝在對象的內部。這一節還是聊聊同步與活躍性和性能問題的一種微妙關係。

緩存未必帶來性能提升

標題裏面我們提到了緩存未必帶來性能,既然這麼說答案是值得深思的,前面章節的將上一次因數分解的計算結果緩存,以便下次可以直接讀取緩存,提升性能,但爲了保證緩存的lastNumber和lastFactors的原子操作,Servlet的方法上增加synchronized關鍵字,試想客戶端必須串行來執行請求。這其實也違背了Servlet設計的初衷。這種併發稱之爲不良併發

其實不難看出,是我們鎖的粒度太粗,導致性能出現的問題。

減小鎖粒度

@ThreadSafe
public class CacheFactorizer implements Servlet{
  @GuardedBy("this") private BigInteger lastNumber;
  @GuardedBy("this") private BigInteger lastFactors;
  @GuardedBy("this") private long hits;
  @GuardedBy("this") private long cacheHits;

  public void service(ServletRequest req,ServletResponse resp){
         BigInteger i = extractFromRequest(req);
         BigInteger factors = null;
         
         synchronized(this){
                hits++;
                if(i.equals(lastNumber)){
                      cacheHits ++;
                      factors = lastFactors.clone();       
                 }
         }
       
         if(factors == null){
              factors = factor(i) ;
              synchronized(this){
                  lastNumber = i;
                   lastFactors = factors;
              }
         }
  }

}

以上代碼有幾個看點:

  • 不難看出,以上程序減小的鎖的粒度,將耗時比較長的factor(i),放在在鎖的外邊。
  • 爲什麼hits、cacheHits計數器不使用原子性的AtomicLong來替換呢?使用多種同步器不僅造成混亂,而且對性能提升也帶來不了什麼優勢。
  • 爲什麼是 factors = lastFactors.clone(); 而不是 factors = lastFactors;?其實這裏涉及到一個概念方法逃逸,即lastFactors從方法中逃逸出去,方法以外也能對該變量做修改,導致lastFactors不可控。

合理的同步代碼塊大小

要判斷合理代碼塊的大小,就需要在安全性、簡單性、性能之間平衡。

  • 安全性:即所見即所知,程序表現出的行爲和狀態期望一致。說白一點就是邏輯沒毛病。這一點必須要保證,不能捨棄。
  • 簡單性:往往滿足簡單性,犧牲的往往是性能。比如我們在方法上增加synchronized關鍵字,邏輯清晰簡單。
  • 性能:在安全性保證的前提下,減小鎖的粒度,用複雜性換取了性能。
    因此,在設計同步代碼時,要在簡單性和性能做出平衡選擇。

耗時長的代碼塊不要使用鎖

  • 造成線程都在等待,鎖的等待時間太長,有可能導致事務未結束數據庫連接池的鏈接佔用不釋放、微服務下的雪崩等。
  • 還有可能造成死鎖,之前提到的銀行轉賬死鎖的例子,如果同步代碼的時間過長,大大提升了死鎖的概率。
    因此,耗時長的代碼塊,會帶來性能和活躍性問題。

喜歡連載可關注簡書或者微信公衆號
簡書專題:Java併發編程實戰-可愛豬豬解讀
https://www.jianshu.com/c/ac717321a386
微信公衆號:可愛豬豬聊程序

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