Synchronize和ReentrantLock區別

目錄介紹

  • 1.Synchronize和ReentrantLock區別php

    • 1.1 類似點
    • 1.2 區別
    • 1.3 什麼是線程安全問題?如何理解
    • 1.4 線程安全須要保證幾個基本特性
  • 2.Synchronize在編譯時如何實現鎖機制
  • 3.ReentrantLock使用方法
  • 4.ReentrantLock鎖機制測試案例分析java

    • 4.1 代碼案例分析
    • 4.2 何時選擇用ReentrantLock
    • 4.3 公平鎖和非公平鎖有何區別
  • 5.問答測試題git

    • 5.1 ReentrantLock和synchronized使用分析

好消息

  • 博客筆記大彙總【16年3月到至今】,包括Java基礎及深刻知識點,Android技術博客,Python學習筆記等等,還包括平時開發中遇到的bug彙總,固然也在工做之餘收集了大量的面試題,長期更新維護而且修正,持續完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計47篇[近20萬字],轉載請註明出處,謝謝!
  • 連接地址:https://github.com/yangchong2...
  • 若是以爲好,能夠star一下,謝謝!固然也歡迎提出建議,萬事起於忽微,量變引發質變!

關於鎖機制文章

  • 01.Synchronize深刻解析github

    • Synchronize深刻解析,sychonized method 和 synchonized代碼塊的效率問題
  • 02.Synchronize和ReentrantLock區別面試

    • Synchronize和ReentrantLock區別,Synchronize在編譯時如何實現鎖機制,ReentrantLock鎖機制測試案例分析,公平鎖和非公平鎖有何區別等等
  • 03.死鎖的發生,定位與修復segmentfault

    • 死鎖的概念和產生死鎖的根本緣由是什麼?死鎖的預防策略中資源有序分配策略是什麼。死鎖發生的場景,死鎖的危害,出現死鎖須要知足條件分析,如何預防死鎖,如何定位死鎖,以及死鎖修復方案分析等等

1.Synchronize和ReentrantLock區別

1.1 類似點:

  • 這兩種同步方式有不少類似之處,它們都是加鎖方式同步,並且都是阻塞式的同步,也就是說當若是一個線程得到了對象鎖,進入了同步塊,其餘訪問該同步塊的線程都必須阻塞在同步塊外面等待,而進行線程阻塞和喚醒的代價是比較高的(操做系統須要在用戶態與內核態之間來回切換,代價很高,不過能夠經過對鎖優化進行改善)。

1.2 區別:

1.2.1 API層面
  • 這兩種方式最大區別就是對於Synchronized來講,它是java語言的關鍵字,是原生語法層面的互斥,須要jvm實現。而ReentrantLock它是JDK 1.5以後提供的API層面的互斥鎖,須要lock()和unlock()方法配合try/finally語句塊來完成。
  • synchronized既能夠修飾方法,也能夠修飾代碼塊。安全

    //synchronized修飾一個方法時,這個方法叫同步方法。
    public synchronized void test() {
    //方法體``
    
    }
    
    synchronized(Object) {
    //括號中表示須要鎖的對象.
    //線程執行的時候會對Object上鎖
    }
  • ReentrantLock使用markdown

    private ReentrantLock lock = new ReentrantLock();
    public void run() {
        lock.lock();
        try{
            for(int i=0;i<5;i++){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }finally{
            lock.unlock();
        }
    }
1.2.2 等待可中斷
  • 等待可中斷是指當持有鎖的線程長期不釋放鎖的時候,正在等待的線程能夠選擇放棄等待,改成處理其餘事情。可等待特性對處理執行時間很是長的同步快頗有幫助。
  • 具體來講,假如業務代碼中有兩個線程,Thread1 Thread2。假設 Thread1 獲取了對象object的鎖,Thread2將等待Thread1釋放object的鎖。多線程

    • 使用synchronized。若是Thread1不釋放,Thread2將一直等待,不能被中斷。synchronized也能夠說是Java提供的原子性內置鎖機制。內部鎖扮演了互斥鎖(mutual exclusion lock ,mutex)的角色,一個線程引用鎖的時候,別的線程阻塞等待。
    • 使用ReentrantLock。若是Thread1不釋放,Thread2等待了很長時間之後,能夠中斷等待,轉而去作別的事情。
1.2.3 公平鎖
  • 公平鎖是指多個線程在等待同一個鎖時,必須按照申請的時間順序來依次得到鎖;而非公平鎖則不能保證這一點。非公平鎖在鎖被釋放時,任何一個等待鎖的線程都有機會得到鎖。
  • synchronized的鎖是非公平鎖,ReentrantLock默認狀況下也是非公平鎖,但能夠經過帶布爾值的構造函數要求使用公平鎖。jvm

    • ReentrantLock 構造器的一個參數是boolean值,它容許您選擇想要一個公平(fair)鎖,仍是一個不公平(unfair)鎖。公平鎖:使線程按照請求鎖的順序依次得到鎖, 可是有成本;不公平鎖:則容許討價還價
    • 那麼如何用代碼設置公平鎖呢?以下所示
    • image
1.2.4 鎖綁定多個條件
  • ReentrantLock能夠同時綁定多個Condition對象,只需屢次調用newCondition方法便可。
  • synchronized中,鎖對象的wait()和notify()或notifyAll()方法能夠實現一個隱含的條件。但若是要和多於一個的條件關聯的時候,就不得不額外添加一個鎖。

1.3 什麼是線程安全問題?如何理解

  • 若是你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。若是每次運行結果和單線程運行的結果是同樣的,並且其餘的變量的值也和預期的是同樣的,就是線程安全的,或者說:一個類或者程序所提供的接口對於線程來講是原子操做或者多個線程之間的切換不會致使該接口的執行結果存在二義性,也就是說咱們不用考慮同步的問題 。

1.4 線程安全須要保證幾個基本特性

  • 一、原子性,簡單說就是相關操做不會中途被其餘線程干擾,通常經過同步機制實現。
  • 二、可見性,是一個線程修改了某個共享變量,其狀態可以當即被其餘線程知曉,一般被解釋爲將線程本地狀態反映到主內存上,volatile 就是負責保證可見性的。
  • 三、有序性,是保證線程內串行語義,避免指令重排等。

2.Synchronize在編譯時如何實現鎖機制

  • Synchronized進過編譯,會在同步塊的先後分別造成monitorenter和monitorexit這個兩個字節碼指令。在執行monitorenter指令時,首先要嘗試獲取對象鎖。若是這個對象沒被鎖定,或者當前線程已經擁有了那個對象鎖,把鎖的計算器加1,相應的,在執行monitorexit指令時會將鎖計算器就減1,當計算器爲0時,鎖就被釋放了。若是獲取對象鎖失敗,那當前線程就要阻塞,直到對象鎖被另外一個線程釋放爲止。

3.ReentrantLock使用方法

  • ReentrantLock是java.util.concurrent包下提供的一套互斥鎖,相比Synchronized,ReentrantLock類提供了一些高級功能,主要有如下3項:

    • 1.等待可中斷,持有鎖的線程長期不釋放的時候,正在等待的線程能夠選擇放棄等待,這至關於Synchronized來講能夠避免出現死鎖的狀況。
    • 2.公平鎖,多個線程等待同一個鎖時,必須按照申請鎖的時間順序得到鎖,Synchronized鎖非公平鎖,ReentrantLock默認的構造函數是建立的非公平鎖,能夠經過參數true設爲公平鎖,但公平鎖表現的性能不是很好。
    • 3.鎖綁定多個條件,一個ReentrantLock對象能夠同時綁定對個對象。
  • 使用方法代碼以下

    private ReentrantLock lock = new ReentrantLock();
    public void run() {
        lock.lock();
        try{
            for(int i=0;i<5;i++){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }finally{
            lock.unlock();
        }
    }
  • 注意問題:爲保證鎖釋放,每個 lock() 動做,建議都當即對應一都當即對應一個 try-catch-finally

4.ReentrantLock鎖機制測試案例分析

4.1 代碼案例分析

  • 代碼以下所示

    private void test2() {
        Runnable t1 = new MyThread();
        new Thread(t1,"t1").start();
        new Thread(t1,"t2").start();
    }
    
    class MyThread implements Runnable {
        private ReentrantLock lock = new ReentrantLock();
        public void run() {
            lock.lock();
            try{
                for(int i=0;i<5;i++){
                    System.out.println(Thread.currentThread().getName()+":"+i);
                }
            }finally{
                lock.unlock();
            }
        }
    }
    
    //打印值以下所示
    10-17 17:06:59.222 6531-6846/com.yc.cn.ycbaseadapter I/System.out: t1:0
    10-17 17:06:59.222 6531-6846/com.yc.cn.ycbaseadapter I/System.out: t1:1
    10-17 17:06:59.222 6531-6846/com.yc.cn.ycbaseadapter I/System.out: t1:2
    10-17 17:06:59.222 6531-6846/com.yc.cn.ycbaseadapter I/System.out: t1:3
    10-17 17:06:59.222 6531-6846/com.yc.cn.ycbaseadapter I/System.out: t1:4
    10-17 17:06:59.224 6531-6847/com.yc.cn.ycbaseadapter I/System.out: t2:0
    10-17 17:06:59.225 6531-6847/com.yc.cn.ycbaseadapter I/System.out: t2:1
    10-17 17:06:59.225 6531-6847/com.yc.cn.ycbaseadapter I/System.out: t2:2
    10-17 17:06:59.225 6531-6847/com.yc.cn.ycbaseadapter I/System.out: t2:3
    10-17 17:06:59.225 6531-6847/com.yc.cn.ycbaseadapter I/System.out: t2:4

4.2 何時選擇用ReentrantLock

  • 適用場景:時間鎖等候、可中斷鎖等候、無塊結構鎖、多個條件變量或者鎖投票
  • 在確實須要一些 synchronized所沒有的特性的時候,好比時間鎖等候、可中斷鎖等候、無塊結構鎖、多個條件變量或者鎖投票。 ReentrantLock 還具備可伸縮性的好處,應當在高度爭用的狀況下使用它,可是請記住,大多數 synchronized 塊幾乎歷來沒有出現過爭用,因此能夠把高度爭用放在一邊。我建議用 synchronized 開發,直到確實證實 synchronized 不合適,而不要僅僅是假設若是使用 ReentrantLock 「性能會更好」。請記住,這些是供高級用戶使用的高級工具。(並且,真正的高級用戶喜歡選擇可以找到的最簡單工具,直到他們認爲簡單的工具不適用爲止。)。一如既往,首先要把事情作好,而後再考慮是否是有必要作得更快。
  • 使用場景代碼展現【摘自ThreadPoolExecutor類,這個類中不少地方用到了這個鎖。本身能夠查看】:

    /**
     * Rolls back the worker thread creation.
     * - removes worker from workers, if present
     * - decrements worker count
     * - rechecks for termination, in case the existence of this
*/
private void addWorkerFailed(Worker w) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        if (w != null)
            workers.remove(w);
        decrementWorkerCount();
        tryTerminate();
    } finally {
        mainLock.unlock();
    }
}
```

4.3 公平鎖和非公平鎖有何區別

  • 公平性是指在競爭場景中,當公平性爲真時,會傾向於將鎖賦予等待時間最久的線程。公平性是減小線程「飢餓」(個別線程長期等待鎖,但始終沒法獲取)狀況發生的一個辦法。

    • 一、公平鎖能保證:老的線程排隊使用鎖,新線程仍然排隊使用鎖。
    • 二、非公平鎖保證:老的線程排隊使用鎖;可是沒法保證新線程搶佔已經在排隊的線程的鎖。
    • 看下面代碼案例所示:能夠得出結論,公平鎖指的是哪一個線程先運行,那就能夠先獲得鎖。非公平鎖是無論線程是不是先運行,新的線程都有可能搶佔已經在排隊的線程的鎖。
    private void test3() {
        Service service = new Service();
        ThreadClass tcArray[] = new ThreadClass[10];
        for(int i=0;i<10;i++){
            tcArray[i] = new ThreadClass(service);
            tcArray[i].start();
        }
    }
    
    public class Service {
        ReentrantLock lock = new ReentrantLock(true);
        Service() {
        }
    
        void getThreadName() {
            System.out.println(Thread.currentThread().getName() + " 已經被鎖定");
        }
    }
    public class ThreadClass extends Thread{
        private Service service;
        ThreadClass(Service service) {
            this.service = service;
        }
        public void run(){
            System.out.println(Thread.currentThread().getName() + " 搶到了鎖");
            service.lock.lock();
            service.getThreadName();
            service.lock.unlock();
        }
    }
    //當ReentrantLock設置true,也就是公平鎖時
    10-17 19:32:22.422 6459-6523/com.yc.cn.ycbaseadapter I/System.out: Thread-5 搶到了鎖
    10-17 19:32:22.422 6459-6523/com.yc.cn.ycbaseadapter I/System.out: Thread-5 已經被鎖定
    10-17 19:32:22.424 6459-6524/com.yc.cn.ycbaseadapter I/System.out: Thread-6 搶到了鎖
    10-17 19:32:22.424 6459-6524/com.yc.cn.ycbaseadapter I/System.out: Thread-6 已經被鎖定
    10-17 19:32:22.427 6459-6525/com.yc.cn.ycbaseadapter I/System.out: Thread-7 搶到了鎖
    10-17 19:32:22.427 6459-6526/com.yc.cn.ycbaseadapter I/System.out: Thread-8 搶到了鎖
    10-17 19:32:22.427 6459-6525/com.yc.cn.ycbaseadapter I/System.out: Thread-7 已經被鎖定
    10-17 19:32:22.427 6459-6526/com.yc.cn.ycbaseadapter I/System.out: Thread-8 已經被鎖定
    10-17 19:32:22.427 6459-6527/com.yc.cn.ycbaseadapter I/System.out: Thread-9 搶到了鎖
    10-17 19:32:22.427 6459-6527/com.yc.cn.ycbaseadapter I/System.out: Thread-9 已經被鎖定
    10-17 19:32:22.428 6459-6528/com.yc.cn.ycbaseadapter I/System.out: Thread-10 搶到了鎖
    10-17 19:32:22.428 6459-6528/com.yc.cn.ycbaseadapter I/System.out: Thread-10 已經被鎖定
    10-17 19:32:22.429 6459-6529/com.yc.cn.ycbaseadapter I/System.out: Thread-11 搶到了鎖
    10-17 19:32:22.429 6459-6529/com.yc.cn.ycbaseadapter I/System.out: Thread-11 已經被鎖定
    10-17 19:32:22.430 6459-6530/com.yc.cn.ycbaseadapter I/System.out: Thread-12 搶到了鎖
    10-17 19:32:22.430 6459-6530/com.yc.cn.ycbaseadapter I/System.out: Thread-12 已經被鎖定
    10-17 19:32:22.431 6459-6532/com.yc.cn.ycbaseadapter I/System.out: Thread-14 搶到了鎖
    10-17 19:32:22.431 6459-6532/com.yc.cn.ycbaseadapter I/System.out: Thread-14 已經被鎖定
    10-17 19:32:22.432 6459-6531/com.yc.cn.ycbaseadapter I/System.out: Thread-13 搶到了鎖
    10-17 19:32:22.433 6459-6531/com.yc.cn.ycbaseadapter I/System.out: Thread-13 已經被鎖定
    
    
    //當ReentrantLock設置false,也就是非公平鎖時
    10-17 19:34:58.102 7089-7183/com.yc.cn.ycbaseadapter I/System.out: Thread-5 搶到了鎖
    10-17 19:34:58.102 7089-7184/com.yc.cn.ycbaseadapter I/System.out: Thread-6 搶到了鎖
    10-17 19:34:58.103 7089-7183/com.yc.cn.ycbaseadapter I/System.out: Thread-5 已經被鎖定
    10-17 19:34:58.103 7089-7185/com.yc.cn.ycbaseadapter I/System.out: Thread-7 搶到了鎖
    10-17 19:34:58.103 7089-7185/com.yc.cn.ycbaseadapter I/System.out: Thread-7 已經被鎖定
    10-17 19:34:58.103 7089-7184/com.yc.cn.ycbaseadapter I/System.out: Thread-6 已經被鎖定
    10-17 19:34:58.104 7089-7186/com.yc.cn.ycbaseadapter I/System.out: Thread-8 搶到了鎖
    10-17 19:34:58.105 7089-7186/com.yc.cn.ycbaseadapter I/System.out: Thread-8 已經被鎖定
    10-17 19:34:58.108 7089-7187/com.yc.cn.ycbaseadapter I/System.out: Thread-9 搶到了鎖
    10-17 19:34:58.108 7089-7187/com.yc.cn.ycbaseadapter I/System.out: Thread-9 已經被鎖定
    10-17 19:34:58.111 7089-7188/com.yc.cn.ycbaseadapter I/System.out: Thread-10 搶到了鎖
    10-17 19:34:58.112 7089-7188/com.yc.cn.ycbaseadapter I/System.out: Thread-10 已經被鎖定
    10-17 19:34:58.112 7089-7189/com.yc.cn.ycbaseadapter I/System.out: Thread-11 搶到了鎖
    10-17 19:34:58.113 7089-7189/com.yc.cn.ycbaseadapter I/System.out: Thread-11 已經被鎖定
    10-17 19:34:58.113 7089-7193/com.yc.cn.ycbaseadapter I/System.out: Thread-14 搶到了鎖
    10-17 19:34:58.113 7089-7193/com.yc.cn.ycbaseadapter I/System.out: Thread-14 已經被鎖定
    10-17 19:34:58.115 7089-7190/com.yc.cn.ycbaseadapter I/System.out: Thread-12 搶到了鎖
    10-17 19:34:58.115 7089-7190/com.yc.cn.ycbaseadapter I/System.out: Thread-12 已經被鎖定
    10-17 19:34:58.116 7089-7191/com.yc.cn.ycbaseadapter I/System.out: Thread-13 搶到了鎖
    10-17 19:34:58.116 7089-7191/com.yc.cn.ycbaseadapter I/System.out: Thread-13 已經被鎖定

5.問答測試題

5.1 ReentrantLock和synchronized使用分析

  • ReentrantLock是Lock的實現類,是一個互斥的同步器,在多線程高競爭條件下,ReentrantLock比synchronized有更加優異的性能表現。
  • 1 用法比較

    • Lock使用起來比較靈活,可是必須有釋放鎖的配合動做
    • Lock必須手動獲取與釋放鎖,而synchronized不須要手動釋放和開啓鎖
    • Lock只適用於代碼塊鎖,而synchronized可用於修飾方法、代碼塊等
  • 2 特性比較

    • ReentrantLock的優點體如今:

      • 具有嘗試非阻塞地獲取鎖的特性:當前線程嘗試獲取鎖,若是這一時刻鎖沒有被其餘線程獲取到,則成功獲取並持有鎖
      • 能被中斷地獲取鎖的特性:與synchronized不一樣,獲取到鎖的線程可以響應中斷,當獲取到鎖的線程被中斷時,中斷異常將會被拋出,同時鎖會被釋放
      • 超時獲取鎖的特性:在指定的時間範圍內獲取鎖;若是截止時間到了仍然沒法獲取鎖,則返回
  • 3 注意事項

    • 在使用ReentrantLock類的時,必定要注意三點:

      • 在finally中釋放鎖,目的是保證在獲取鎖以後,最終可以被釋放
      • 不要將獲取鎖的過程寫在try塊內,由於若是在獲取鎖時發生了異常,異常拋出的同時,也會致使鎖無端被釋放。
      • ReentrantLock提供了一個newCondition的方法,以便用戶在同一鎖的狀況下能夠根據不一樣的狀況執行等待或喚醒的動做。

關於其餘內容介紹

01.關於博客彙總連接

02.關於個人博客

相關文章
相關標籤/搜索