Java之多線程鎖的10點整理和建議

開心一笑

【老婆出差,送老婆去車站盼玄,本想送到站里面的贴彼,誰知檢票員不讓進,老婆在里面瞅了瞅我哈哈大笑道:“來呀埃儿,來打我捌髡獭!你進不來蝌箍!”青灼。知道老婆一直是個二貨的我無奈地笑笑,誰知檢票員開口了:你進去吧妓盲!這次給你個特例杂拨!我。悯衬。弹沽。檀夹。。策橘≌ǘ桑】

提出問題

java中關于鎖的幾點總結和建議???

唯美圖片

解決問題

前言

下面的內(nèi)容有些概念解釋得有點簡單。但是丽已,我不想解釋一堆枯燥的概念蚌堵。如果有哪些解釋的不是很清楚,請諒解沛婴,然后找度娘吼畏。本文中的很多例子是來自網(wǎng)上的,并非有意抄襲嘁灯。

1.線程安全和非線程安全概念

“非線程安全”的問題存在于“實例變量”中泻蚊,如果是方法內(nèi)部的私有變量,則不存在“非線程安全“的問題丑婿。

2.方法鎖性雄,對象鎖,類鎖概念

方法鎖:synchronized 修飾方法時羹奉。

對象鎖:synchronized 修飾方法或代碼塊秒旋。

類鎖:synchronized 修飾靜態(tài)的方法或代碼塊。

總結:關鍵字加到 static 方法上是給 Class 上鎖诀拭,加到非 static 方法上是給對象上鎖滩褥。class 鎖可以對類的所有實例起作用,即就是在任何時候炫加,只能有一個線程訪問該類的 static 方法哀托。因為 static 方法就是類方法残黑。

3.synchronized需要注意的幾點

  • synchronized 取得的鎖都是對象鎖撇吞,而不是代碼浮声。
  • synchronized 關鍵字是不能繼承的「陈粒基類的方法 synchronized ay(){} 在繼承類中并不自動是 synchronized ay(){}插勤,而是變成了ay(){}。繼承類需要你顯式的指定它的某個方法為 synchronized 方法革骨。
  • 一個線程訪問了 synchronized 同步代碼塊中的代碼农尖,另一個線程不可以訪問該對象的任何同步代碼塊,但可以訪問非同步代碼塊良哲。同步代碼塊鎖定的也是當前對象

4. 鎖優(yōu)化的思路

鎖優(yōu)化的思路和方法總結一下盛卡,有以下幾種。

  • 減少鎖持有時間(盡量縮小鎖的范圍)
  • 減小鎖粒度
  • 鎖分離
  • 鎖粗化
  • 鎖消除

5.盡量縮小鎖的范圍

我們應該確保我們只在必要的地方加鎖筑凫,將鎖從方法聲明移到方法體中會延遲鎖的加載滑沧,進而降低了鎖競爭的可能性并村。先看下面的實例:

class SynObj {

    //方法鎖/或者對象鎖
    public synchronized void methodA() {
        System.out.println("methodA.....");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public  void methodB() {
        //對代碼塊進行鎖,降低鎖的競爭
        synchronized(this) {
            System.out.println("methodB.....");
        }
    }

    public void methodC() {
        String str = "sss";
        //這里鎖的是 str 這個對象滓技,而不是 SynObj 對象
        synchronized (str) {
            System.out.println("methodC.....");
        }
    }
}

/**
 * Created by Ay on 2017/3/26.
 */
public class AyTest {

    public static void main(String[] args) {
        final SynObj obj = new SynObj();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                obj.methodA();
            }
        });
        t1.start();

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                obj.methodB();
            }
        });
        t2.start();

        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                obj.methodC();
            }
        });
        t3.start();
    }
}

打印結果:

methodA.....
methodC.....
//methodB會隔一段時間才會打印出來
methodB.....

總結:因為哩牍,一個線程訪問了 synchronized 同步代碼塊中的代碼,另一個線程不可以訪問該對象的任何同步代碼塊令漂,但可以訪問非同步代碼塊膝昆。所有縮小鎖的范圍可以在一定程度上提高代碼性能。

6.減少鎖持有時間

這個例子其實和上面的盡量縮小鎖的范圍意思相差無二叠必,也很簡單外潜,具體看下面例子,就不多說了挠唆。

public synchronized void syncMethod(){
    method1();
    method2();
    method3();
}

public void syncMethod(){
    method1();
    //減少鎖持有的時間
    synchronized(this){
        method2();
    }
    method3();
} 

7.減小鎖粒度

加鎖粒度:所謂加鎖粒度就是你要鎖住的范圍是多大。

例如:你在家上衛(wèi)生間嘱吗,你只要鎖住衛(wèi)生間就可以了吧玄组,不需要將整個家都鎖起來不讓家人進門吧,衛(wèi)生間就是你的加鎖粒度谒麦。

合理的加鎖粒度:

其實衛(wèi)生間并不只是用來上廁所的俄讹,還可以洗澡,洗手绕德。這里就涉及到優(yōu)化加鎖粒度的問題患膛。你在衛(wèi)生間里洗澡,其實別人也可以同時去里面洗手耻蛇,只要做到隔離起來就可以踪蹬,如果馬桶,浴缸臣咖,洗漱臺都是隔開相對獨立的跃捣,實際上衛(wèi)生間可以同時給三個人使用,當然三個人做的事兒不能一樣夺蛇。這樣就細化了加鎖粒度疚漆,你在洗澡的時候只要關上浴室的門,別人還是可以進去洗手的刁赦。如果當初設計衛(wèi)生間的時候沒有將不同的功能區(qū)域劃分隔離開娶聘,就不能實現(xiàn)衛(wèi)生間資源的最大化使用。這就是設計架構的重要性甚脉。

Java相關鎖粒度注意點:

  • 1.讀寫分開(CopyOnWrite)
  • 2.將鎖打散(ConcurrentHashMap分解Segment)
  • 3.通過synchronized或Lock來鎖定一段代碼區(qū)域時丸升,除了考慮它們鎖定的對象是什么以外,還需要考慮是否可以將范圍縮小一些牺氨。
  • 4.定義多個對象发钝,然后讓不同的方法鎖住不同的對象顿涣。
  • 5.拆分子方法,對某個必要的子方法加鎖酝豪;通過鎖塊來隔離部分代碼段涛碑。
  • 6.JVM對鎖的優(yōu)化有一個粗粒度的動作,我們自己寫代碼時盡量不依賴于JVM這種優(yōu)化機制孵淘。
  • 7.用樂觀替代悲觀蒲障,如同步之前先做某些條件判斷。

8.鎖分離

  • 最常見的鎖分離就是讀寫鎖ReadWriteLock瘫证,根據(jù)功能進行分離成讀鎖和寫鎖揉阎,這樣讀讀不互斥,讀寫互斥背捌,寫寫互斥毙籽,即保證了線程安全,又提高了性能毡庆。

還有就是網(wǎng)上一個高手寫的一個例子:

public class Grocery {
    private final ArrayList fruits = new ArrayList();
    private final ArrayList vegetables = new ArrayList();
    //對象鎖坑赡,不好,效率低
    public synchronized void addFruit(int index, String fruit) {
        fruits.add(index, fruit);
    }
    //對象鎖么抗,不好毅否,效率低
    public synchronized void removeFruit(int index) {
        fruits.remove(index);
    }
    //對象鎖,不好蝇刀,效率低
    public synchronized void addVegetable(int index, String vegetable) {
        vegetables.add(index, vegetable);
    }
    //對象鎖螟加,不好,效率低
    public synchronized void removeVegetable(int index) {
        vegetables.remove(index);
    }
}

優(yōu)化后:

public class Grocery {
    private final ArrayList fruits = new ArrayList();
    private final ArrayList vegetables = new ArrayList();
    public void addFruit(int index, String fruit) {
        //水果鎖 
        synchronized(fruits) fruits.add(index, fruit);
    }
    public void removeFruit(int index) {
        //水果鎖 
        synchronized(fruits) {fruits.remove(index);}
    }
    public void addVegetable(int index, String vegetable) {
        //蔬菜鎖
        synchronized(vegetables) vegetables.add(index, vegetable);
    }
    public void removeVegetable(int index) {
        //蔬菜鎖
        synchronized(vegetables) vegetables.remove(index);
    }
}

9.鎖粗化

通常情況下吞琐,為了保證多線程間的有效并發(fā)捆探,會要求每個線程持有鎖的時間盡量短,即在使用完公共資源后站粟,應該立即釋放鎖徐许。只有這樣,等待在這個鎖上的其他線程才能盡早的獲得資源執(zhí)行任務卒蘸。但是雌隅,凡事都有一個度,如果對同一個鎖不停的進行請求缸沃、同步和釋放恰起,其本身也會消耗系統(tǒng)寶貴的資源,反而不利于性能的優(yōu)化 趾牧。

舉個例子:

public void demoMethod(){  
    synchronized(lock){   
        //do sth.  
    }  
    //做其他不需要的同步的工作检盼,但能很快執(zhí)行完畢  
    synchronized(lock){   
        //do sth.  
    } 
}

這種情況,根據(jù)鎖粗化的思想翘单,應該合并

public void demoMethod(){  
    //整合成一次鎖請求 
    synchronized(lock){   
        //do sth.   
        //做其他不需要的同步的工作吨枉,但能很快執(zhí)行完畢  
    }
}

當然這是有前提的蹦渣,前提就是中間的那些不需要同步的工作是很快執(zhí)行完成的。
再舉一個極端的例子:

for(int i=0;i<CIRCLE;i++){  
    synchronized(lock){  
        
    } 
}

在一個循環(huán)內(nèi)不同得獲得鎖貌亭。雖然JDK內(nèi)部會對這個代碼做些優(yōu)化柬唯,但是還不如直接寫成

synchronized(lock){ 
    for(int i=0;i<CIRCLE;i++){ 
        
    } 
}

當然如果有需求說,這樣的循環(huán)太久圃庭,需要給其他線程不要等待太久锄奢,那只能寫成上面那種。如果沒有這樣類似的需求剧腻,還是直接寫成下面那種比較好拘央。

10 鎖原則的總結

總結:減少對鎖的等待時間的最有效方法是減少這個鎖所保護的范圍大小。下面是一些準則:

  • 減少對任何鎖的請求頻率书在。
  • 只鎖定訪問共享數(shù)據(jù)的代碼灰伟,而不是一個組件的所有代碼(這將減少鎖的持有時間)。
  • 只鎖定特定的數(shù)據(jù)項或結構儒旬,而不是整個例程栏账。
  • 始終將鎖和特定的數(shù)據(jù)項或結構關聯(lián)起來,而不是和例程關聯(lián)义矛。
  • 對于大的數(shù)據(jù)結構,為結構的每一元素選擇一個鎖盟萨,而不是為整個結構選擇一個鎖凉翻。
  • 當持有一個鎖時,從不執(zhí)行同步 I/O 或者任何其他阻塞活動捻激。
  • 如果您對您組件中的同一數(shù)據(jù)有多個訪問制轰,請試著將它們移到一起,以便它們可以包含在一個鎖定 - 解鎖操作中胞谭。
  • 避免雙喚醒的情況垃杖。如果您在一個鎖下修改了一些數(shù)據(jù),并且不得不通知某人您做了這件事丈屹,那么在公布喚醒之前請釋放該鎖调俘。
  • 如果必須同時持有兩個鎖,那么最后請求那個最忙的鎖旺垒。

讀書感悟

來自村上春樹《1Q84》

  • 孤獨一人也沒關系彩库,只要能發(fā)自內(nèi)心地愛著一個人,人生就會有救先蒋。哪怕不能和他生活在一起骇钦。
  • 有希望之處定有磨練。
  • 我能承受任何痛苦竞漾,只要這種痛苦有意義眯搭。
  • 如果能真心愛上一個人窥翩,那么不管對方是何等惡劣,哪怕對方并不愛自己鳞仙,人生也至少不會是地獄寇蚊,就算多少有點黯淡。
  • 世上的人大半不會用自己的腦袋思考繁扎,而且越是不思考的人幔荒,越不愿傾聽別人說話。
  • 我討厭別人的施舍梳玫,因此要盡可能多地施與于人爹梁。
  • 有再多的才能,也未必能換來一頓飽飯提澎;但是有優(yōu)秀的直覺姚垃,就完全衣食無憂了。

經(jīng)典故事

禪師愛蘭花:有一位金代禪師非常喜愛蘭花盼忌,在平日弘法講經(jīng)之余积糯,花費了許多的時間栽種蘭花。有一天谦纱,他要外出云游一段時間看成,臨行前交待弟子?要好好照顧寺里的蘭花。在這段期間跨嘉,弟子們總是細心照顧蘭花川慌,但有一天在澆水時卻不小心將蘭花架碰倒了,所有的蘭花盆都跌碎了祠乃,蘭花散了滿地梦重。弟子們都因此非常恐慌亮瓷,打算等師父回來后琴拧,向師父賠罪領罰。金代禪師回來了嘱支,聞知此事蚓胸,便召集弟子們,不但沒有責怪除师,反而說道:“我種蘭花赢织,一來是希望用來供佛,二來也是為了美化寺廟環(huán)境馍盟,不是為了生氣而種蘭花的于置。】

大神文章

【1】《java多線程編程核心技術》讀書筆記2.1
【2】Java多線程編程核心技術--第二章
【3】方法鎖,對象鎖八毯,類鎖區(qū)別
【4】java中的synchronized(同步代碼塊和同步方法的區(qū)別)
【5】改善Java中鎖的性能
【6】[高并發(fā)Java 九] 鎖的優(yōu)化和注意事項
【7】并發(fā)性能優(yōu)化 – 降低鎖粒度
【8】鎖粒度

其他

如果有帶給你一絲絲小快樂搓侄,就讓快樂繼續(xù)傳遞下去,歡迎點贊话速、頂讶踪、歡迎留下寶貴的意見、多謝支持泊交!

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末乳讥,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子廓俭,更是在濱河造成了極大的恐慌云石,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件研乒,死亡現(xiàn)場離奇詭異汹忠,居然都是意外死亡,警方通過查閱死者的電腦和手機雹熬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門宽菜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人竿报,你說我怎么就攤上這事铅乡。” “怎么了烈菌?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵阵幸,是天一觀的道長。 經(jīng)常有香客問我僧界,道長侨嘀,這世上最難降的妖魔是什么臭挽? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任捂襟,我火速辦了婚禮,結果婚禮上欢峰,老公的妹妹穿的比我還像新娘葬荷。我一直安慰自己,他們只是感情好纽帖,可當我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布宠漩。 她就那樣靜靜地躺著,像睡著了一般懊直。 火紅的嫁衣襯著肌膚如雪扒吁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天室囊,我揣著相機與錄音雕崩,去河邊找鬼魁索。 笑死,一個胖子當著我的面吹牛盼铁,可吹牛的內(nèi)容都是我干的粗蔚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼饶火,長吁一口氣:“原來是場噩夢啊……” “哼鹏控!你這毒婦竟也來了?” 一聲冷哼從身側響起肤寝,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤当辐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后醒陆,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瀑构,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年刨摩,在試婚紗的時候發(fā)現(xiàn)自己被綠了寺晌。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡澡刹,死狀恐怖呻征,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情罢浇,我是刑警寧澤陆赋,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站嚷闭,受9級特大地震影響攒岛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜胞锰,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一灾锯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嗅榕,春花似錦顺饮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至帽蝶,卻和暖如春赦肋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工佃乘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留局蚀,地道東北人。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓恕稠,卻偏偏與公主長得像琅绅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鹅巍,可洞房花燭夜當晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容