java并发编程之4——Java锁分解锁分段技术
轉載自?java并發編程之4——Java鎖分解鎖分段技術
?
并發編程的所有問題,最后都轉換成了,“有狀態bean”的狀態的同步與互斥修改問題。而最后提出的解決“有狀態bean”的同步與互斥修改問題的方案是為所有修改這個狀態的方法都加上鎖,這樣也就可以保證他們在修改bean的狀態的時候是順序進行的。但是這樣整個過程的瓶頸也就是被加鎖的這段代碼。由此就產生了很多對程序加鎖的優化思想,從整體上來看,可以分為兩個部分:對單個鎖的算法的優化。和對鎖粒度的細分。
單個鎖的優化
??????自旋鎖:非自旋鎖在未獲取鎖的情況會被阻塞,之后再喚醒嘗試獲得鎖。而JDK的阻塞和喚醒是基于操作系統實現的,會有系統資源的開銷。自旋鎖就是線程不停地循環嘗試獲得鎖,而不會將自己阻塞,這樣不會浪費系統的資源開銷,但是會浪費CPU的資源。所有現在的JDK都的是先自旋等待,如果自旋等待一段時間之后還沒有獲取到鎖,就會將當前線程阻塞。
??????鎖消除:當JVM分析代碼發現某個方法只被單個線程安全訪問,而且這個方法是同步方法,那么JVM就會去掉這個方法的鎖。
單個鎖優化的瓶頸
??????對單個鎖優化的效果就像提高單個CPU的處理能力一樣,最終會由于各個方面的限制而達到一個平衡點,到達這個點之后優化單個鎖的對高并發下面鎖的優化效果越來越低。所以將一個鎖進行粒度細分帶來的效果會很明顯,如果一個鎖保護的代碼塊被拆分成兩個鎖來保護,那么程序的效率就大約能夠提高到2倍,這個比單個鎖的優化帶來的效果要明顯很多。常見的 鎖粒度細分技術有:鎖分解和鎖分段
鎖粒度細分
??????鎖的粒度細分主要有:鎖分解和鎖分段兩種方式。他們的核心都是降低鎖競爭發生的可能性。
鎖分解
??????鎖分解的實現方式主要有兩種:
縮小鎖的范圍
??????鎖分解的核心是將無關的代碼塊,如果在一個方法中有一部分的代碼與鎖無關,一部分的代碼與鎖有關,那么可以縮小這個鎖的返回,這樣鎖操作的代碼塊就會減少,鎖競爭的可能性也會減少
傳統寫法:
public synchronized void synchronizedOnMethod(){ //粗粒度直接在方法上加synchronized,這樣會提高鎖沖突的概率prefix();try {TimeUnit.SECONDS.sleep(1);}catch (InterruptedException e){}post();}private void post(){try {TimeUnit.SECONDS.sleep(1);}catch (InterruptedException e){}}private void prefix(){try {TimeUnit.SECONDS.sleep(1);}catch (InterruptedException e){}}}修正寫法
//假設prefix和post方法是線程安全的(與鎖無關的代碼) static class SynchronizedClazz{public void mineSynOnMethod(){prefix();synchronized (this){ //synchronized代碼塊只保護有競爭的代碼try {TimeUnit.SECONDS.sleep(1);}catch (InterruptedException e){}}post();}減少鎖的粒度
??????如果一個鎖需要保護多個相互獨立的變量,那么可以將一個鎖分解為多個鎖,并且每個鎖保護一個變量,比較對比下面的兩段代碼,
假設 allUsers和allComputers是兩個相互獨立的變量
傳統寫法
static class DecomposeClazz{private final Set<String> allUsers = new HashSet<String>();private final Set<String> allComputers = new HashSet<String>();public synchronized void addUser(String user){ //公用一把鎖allUsers.add(user);}public synchronized void addComputer(String computer){allComputers.add(computer);}}修正寫法
static class DecompossClazz2{private final Set<String> allUsers = new HashSet<String>();private final Set<String> allComputers = new HashSet<String>();public void addUser(String user){ //分解為兩把鎖synchronized (allUsers){allUsers.add(user);}}public void addComputer(String computer){synchronized (allComputers){allComputers.add(computer);}}}鎖分段
??????如上的方法把一個鎖分解為2個鎖時候,采用兩個線程時候,大約能夠使程序的效率提升一倍,但是當競爭激烈的時候,單一個鎖上面的競爭還是很激烈,我們還可以將鎖分解技術進一步擴展到一組獨立的對象例如ConcurrentHashMap的鎖分段技術
package com.qunar.des.lock; import java.util.HashMap; import java.util.Map; /*** Created by chenglaiguo on 8/14/15.*/ public class MyConcurrentHashMap<K,V> {private final int LOCK_COUNT = 16;private final Map<K,V> map;private final Object[] locks ;public MyConcurrentHashMap() {this.map = new HashMap<K,V>();locks = new Object[LOCK_COUNT];for (int i=0;i<LOCK_COUNT;i++){locks[i] = new Object();}}private int keyHashCode(K k){return Math.abs(k.hashCode() % LOCK_COUNT);}public V get(K k){int keyHashCode = keyHashCode(k);synchronized (locks[keyHashCode % LOCK_COUNT]){return map.get(k);}}}?
總結
以上是生活随笔為你收集整理的java并发编程之4——Java锁分解锁分段技术的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 10秒学会使用剪映电脑版识别歌词剪映的歌
- 下一篇: MySQL数据库开发的 36 条军规