10 鎖的優化及注意事項

2022-10-08 22:15:25 字數 2515 閱讀 1643

目錄10.2 jvm 對鎖的優化

對於使用鎖進行併發控制的應用程式來說,如果單個執行緒特有鎖的時間過長,會導致鎖的競爭更加激烈,會影響系統的效能,在程式中需要盡可能減少執行緒對鎖的持有時間,如下面**:

// 同步方法

public synchronized void syncmethod()

在syncmethod同步方法中,假設只有 mutexmethod()方法是需要同步的,othercode1()方法與 othercode2()方法不需要進行同步;如果othercode1 與 othercode2 這兩個方法需要花費較長的 cpu 時間,在併發量較大的情況下,這種同步方案會導致等待執行緒的大量增加。乙個較好的優化方案是,只在必要時進行同步,可以減少鎖的持有時間,提高系統的吞吐量,如把上面的**改為:

public void syncmethod() 

othercode();

}

只對 mutexmethod()方法進行同步,這種減少鎖持有時間有助於降低鎖衝突的可能性,提公升系統的併發能力。

乙個鎖保護的共享資料的數量大小稱為鎖的粒度;如果乙個鎖保護的共享資料的數量大就稱該鎖的粒度粗,否則稱該鎖的粒度細。鎖的粒度過粗會導致執行緒在申請鎖時需要進行不必要的等待,減少鎖粒度是一種削弱多執行緒鎖競爭的一種手段,可以提高系統的併發性。

在jdk7前,j**a.util.concurrent.concurrenthashmap類採用分段鎖協議,可以提高程式的併發性。

使用 readwritelock讀寫分離鎖可以提高系統效能,使用讀寫分離鎖也是減小鎖粒度的一種特殊情況;第二條建議是能分割資料結構實現減小鎖的粒度,那麼讀寫鎖是對系統功能點的分割。

在多數情況下都允許多個執行緒同時讀,在寫的使用採用獨佔鎖,在讀多寫少的情況下,使用讀寫鎖可以大大提高系統的併發能力。

將讀寫鎖的思想進一步延伸就是鎖分離。讀寫鎖是根據讀寫操作功能上的不同進行了鎖分離,根據應用程式功能的特點,也可以對獨佔鎖進行分離。如:

j**a.util.concurrent.linkedblockingqueue 類中 take() 與 put() 方法分別從隊頭取資料,把資料新增到隊尾。雖然這兩個方法都是對佇列進行修改操作,由於操作的主體是鍊錶,take()操作的是鍊錶的頭部;put()操作的是鍊錶的尾部,兩者並不衝突。如果採用獨佔鎖的話,這兩個操作不能同時併發,在該類中就採用鎖分離,take()取資料時有取鎖,put()新增資料時有自己的新增鎖,這樣 take()與 put()相互獨立實現了併發。

為了保證多執行緒間的有效併發,會要求每個執行緒持有鎖的時間盡量短,但是凡事都有乙個度,如果對同乙個鎖不斷的進行請求同步和釋放,也會消耗系統資源。如:

public void method1() 

synchronized( lock )

}

public void method1() 

}

在開發過程中,也應該有意識的在合理的場合進行鎖的粗化,尤其在迴圈體內請求鎖時,如:

for(int i = 0; i < 100; i++) 

}

這種情況下,意味著每次迴圈都需要申請鎖和釋放鎖,所以一種更合理的做法就是在迴圈外請求一次鎖。如:

synchronized( lock ) 

}

鎖偏向是一種針對加鎖操作的優化,如果乙個執行緒獲得了鎖,那麼鎖就進入偏向模式,當這個執行緒再次請求鎖時,無須再做任何同步操作,這樣可以節省有關鎖申請的時間,提高了程式的效能。

鎖偏向在沒有鎖競爭的場合可以有較好的優化效果,對於鎖競爭比較激烈的場景,效果不佳,鎖競爭激烈的情況下可能是每次都是不同的執行緒來請求鎖,這時偏向模式失效。

如果鎖偏向失敗,jvm 不會立即掛起執行緒,還會使用一種稱為輕量級鎖的優化手段。會將物件的頭部作為指標,指向持有鎖的執行緒堆疊內部,來判斷乙個執行緒是否持有物件鎖。如果執行緒獲得輕量級鎖成功,就進入臨界區,如果獲得輕量級鎖失敗,表示其他執行緒搶到了鎖,那麼當前執行緒的鎖的請求就膨脹為重量級鎖。當前執行緒就轉到阻塞佇列中變為阻塞狀態。

偏向鎖,輕量級鎖都是樂觀鎖,重量級鎖是悲觀鎖乙個物件剛開始例項化時,沒有任何執行緒訪問它,它是可偏向的,即它認為只可能有乙個執行緒來訪問它,所以當第乙個執行緒來訪問它的時候,它會偏向這個執行緒。偏向第乙個執行緒,這個執行緒在修改物件頭成為偏向鎖時使用 cas 操作,將物件頭中 threadid 改成自己的 id,之後再訪問這個物件時,只需要對比 id 即可。一旦有第二個執行緒訪問該物件,因為偏向鎖不會主動釋放,所以第二個執行緒可以檢視物件的偏向狀態,當第二個執行緒訪問物件時,表示在這個物件上已經存在競爭了,檢查原來持有物件鎖的執行緒是否存活,如果掛了則將物件變為無鎖狀態,然後重新偏向新的執行緒;如果原來的執行緒依然存活,則馬上執行原來執行緒的棧,檢查該物件的使用情況,如果仍然需要偏向鎖,則偏向鎖公升級為輕量級鎖。

輕量級鎖認為競爭存在,但是競爭的程度很輕,一般兩個執行緒對同乙個鎖的操作會錯開,或者稍微等待一下(自旋)另外乙個執行緒就會釋放鎖。當自旋超過一定次數,或者乙個執行緒持有鎖,乙個執行緒在自旋,又來第三個執行緒訪問時,輕量級鎖會膨脹為重量級鎖,重量級鎖除了持有鎖的執行緒外,其他的執行緒都阻塞。

Redis 注意事項及使用優化

快取穿透指查詢乙個根本不存在的資料導致快取層和儲存層都無法命中,快取穿透導致每次查詢都要去儲存層查詢,失去了快取層保護後端儲存層的意義 可能造成快取穿透的兩個原因 1.自身 或者資料原因 2.惡意攻擊 或者爬蟲軟體 快取穿透的解決方案 1.空物件快取,當查詢穿透到儲存層,查到為null時,將這個物件...

synchronized鎖的注意事項

該鎖可以重入 即 乙個同步方法呼叫另乙個同步方法,乙個執行緒已經擁有某個物件的鎖,再次申請的時候仍然會得到該物件的鎖。public class test1 public synchronized void fun2 public static void main string args public...

sql 優化注意事項

1.儲存過程比sql語句效率高 儲存過程經過預編譯處理 而sql查詢沒有 2.給表建立索引的字段 常見字段是where 後的條件字段 3.查詢表時使用 nolock 查詢語句不會阻塞 4.不要使用 select from dbo.table 用具體的字段列表代替 不要返回用不到的任何字段。同時避免使...