Java同步鎖-synchronized與lock

1、synchronized關鍵字

一、synchronized簡介

synchronized實現同步的基礎:java中每一個對象均可以做爲鎖。當線程試圖訪問同步代碼時,必須先得到對象鎖,退出或拋出異常時必須釋放鎖。java

表現形式爲:代碼塊同步方法同步緩存


二、synchronized的使用場景

  • 方法同步

public synchronized void method1(){}複製代碼

鎖住的是該對象的一個實例,當不一樣線程調用該實例對象中該同步方法,線程只有一個獲得鎖,其他被阻塞。但若是不一樣線程同時對該類的不一樣實例對象執行該同步方法,則不會阻塞,由於他們使用不一樣的鎖。bash


  • 代碼塊同步

synchronized(this)( //ToDo}
或
synchronized(普通變量){    }複製代碼

同上markdown


  • 靜態方法同步

public synchronized static void method3(){}複製代碼

鎖住的是該類,當不一樣線程調用該類的該static同步方法時,就只能有一個線程得到鎖其他線程被阻塞多線程


  • 靜態代碼塊

synchronized(Test.class){ //ToDo}
或
synchronized(靜態變量){ //ToDo}複製代碼

同上併發


三、synchronized鎖升級

鎖一共有4種狀態,級別從低到高依次是:無鎖狀態偏向鎖狀態輕量級鎖重量級鎖高併發

鎖能夠升級但不能降級,意味着偏向鎖升級爲輕量級鎖後不能降級爲偏向鎖。工具

  • 偏向鎖

大所述狀況下,鎖不只不存在多線程競爭,並且老是由同一線程屢次得到,爲了讓線程得到所的代價更低而引入偏向鎖。oop

當一個線程訪問同步代碼並獲取鎖時,會對對象頭和棧幀中的鎖記錄裏保存鎖偏向的線程ID,之後該線程再進入和退出同步鎖時,不須要進行CAS操做來加鎖和解鎖,而只是簡單地測試一下對象頭的Mark Word裏是否存儲執行當前線程的偏向鎖。性能

偏向鎖做用是:在沒有別的線程競爭的時候,一直偏向當前線程,當前線程能夠一直執行。


  • 輕量級鎖(自旋鎖)

輕量級鎖,由偏向鎖升級而來。

偏向鎖運行在一個線程進入同步塊的狀況下,當第二個線程加入鎖爭用的時候,偏向鎖就會升級爲輕量級鎖。

  • 重量級鎖

輕量級鎖膨脹以後,就升級爲重量級鎖。

重量級鎖時依賴對象內部的monitor鎖來實現的,而monitor又依賴操做系統的MutexLock(互斥鎖)來實現的,因此重量級鎖也被稱爲互斥鎖(synchronized就是重量級鎖)。


偏向鎖

優勢:加鎖和解鎖不須要額外的消耗

缺點:線程存在競爭,會帶來額外的鎖撤銷的消耗

場景:單一線程訪問同步塊場景

輕量級鎖

優勢:競爭的線程不會阻塞,提升了程序的響應速度。

缺點:線程自旋時不釋放CPU

場景:追求響應時間,同步塊執行速度很是快。

重量級鎖

優勢:線程競爭不使用自旋,釋放CPU

缺點:線程阻塞,響應時間緩慢。

場景:追求吞吐量,同步塊執行速度較長。


2、Lock接口

一、Lock接口

Lock,鎖對象。在Lock接口出現以前,Java程序時靠synchronized關鍵字實現鎖功能。而在Java SE5.0以後併發包中新增了Lock接口來實現鎖的功能。

它能提供synchronized關鍵字相似的同步功能,但須要顯示得到鎖和釋放鎖,至於兩者區別後文補充。


Lock接口的主要方法:

  • void lock()

執行該方法時,若鎖處於空閒狀態,當前線程將得到鎖。相反,若是鎖已經被其餘線程持有,則禁止當前線程得到鎖。


  • boolean tryLock()

若鎖可用,則得到鎖,並當即返回true,不然返回false。

tryLock()和Lock()的區別在於:

tryLock()只是嘗試得到鎖,若鎖不可用不會致使當前線程被禁止,當前線程仍然繼續往下執行代碼。

Lock()則是必定要得到鎖,若是鎖不可用,就一直等待,在未得到鎖以前,當前線程並不繼續向下自行。


  • void unlock()

執行該方法時,當前線程將釋放持有鎖,鎖只能由持有者釋放,若線程沒有持有鎖,則執行該方法,可能致使異常方式。


  • Condition newCondition()

條件對象,得到等待通知組件。該組件和當前的鎖綁定,當前線程只有得到鎖,纔會調用該組件的await()方法,調用後,當前線程將釋放鎖。


二、Reentrantlock的使用

Reentrantlock的使用很簡單,只須要顯示調用,得到同步鎖,釋放同步鎖便可。

ReentrantLock lock = new ReentrantLock(); //參數默認false,不公平鎖
.....................

try {
    lock.lock(); //若是被其它資源鎖定,會在此等待鎖釋放,達到暫停的效果
    //操做
}catch(Exception e){
   //異常處理
} finally {
    lock.unlock();  //釋放鎖
}複製代碼

reentrantlock鎖,在高併發的條件下使用的性能遠遠高於synchronized關鍵字。

而且reentratnlock公平和非公平鎖的隊列都是基於鎖內部維護的一個雙向鏈表,表結點Node的值就是每個請求當前鎖的線程。


三、ReadWriteLock接口

ReadWriteLock接口中主要方法以下:

public interface ReadWriteLock {
    /**
     * Returns the lock used for reading.
     *
     * @return the lock used for reading
     */
    Lock readLock();

    /**
     * Returns the lock used for writing.
     *
     * @return the lock used for writing
     */
    Lock writeLock();
}複製代碼

ReadWriteLock管理一組鎖,一個是隻讀鎖,一個是寫鎖。

Java併發庫中ReentrantReadWriteLock實現了ReadWriteLock接口並添加了可重入的特性。


讀鎖同一時刻容許有多個線程在訪問,可是寫進程訪問時,全部的讀線程其餘寫進程被阻塞

讀寫鎖維護一對鎖,一個讀鎖和一個寫鎖,經過讀寫鎖分離,使得併發性相比通常的排它鎖有很大的提高。


ReentrantReadWriteLock讀寫鎖的幾個特性

  • 公平選擇性
  • 重入性
  • 鎖降級


讀寫鎖例子(程序來源於網上:blog.csdn.net/canot/artic…):

public class Cache{
  static Map<String,Object> map = new HashMap<String,Object>();
  static ReentrantReadWriteLock  rwl = new ReentrantReadWriteLock();
  static Lock rLock = rwl.readLock();
  static Lock wLock = rwl.writeLock();

  //獲取一個key對應的value
  public static final Object get(String key){
     r.lock();
     try{
         return map.get(key);
     }finally{
         r.unlock();
     }
  }

  //設置key對應的value並返回舊的value
  public static fianl Object put(String key,Object value){
     w.lock();
     try{
        return map.put(key,value);
     }final{
        w.unlock();
     }
  }

  //清空緩存
  public static fianl void clear(){
     w.lock();
     try{
        map.clear();
     } finally{
        w.unlock();
     }
  }
}複製代碼


  • 讀寫鎖的鎖降級

鎖降級是指寫鎖降級成爲讀鎖。若是當前線程持有寫鎖,而後將其釋放再獲取讀鎖的過程不能稱爲鎖降級。鎖降級指的在持有寫鎖的時候再獲取讀鎖,獲取到讀鎖後釋放以前寫鎖的過程稱爲鎖釋放。

(程序來源於:blog.csdn.net/qq_38737992…

public void work() {
        reentrantReadWriteLock.readLock().lock();
 
        if (!update) {
            reentrantReadWriteLock.readLock().unlock();
 
            // 鎖降級開始
            reentrantReadWriteLock.writeLock().lock();
            try {
                if (!update) {
                    // 準備數據
                    ++index;
                    try {
                        TimeUnit.MILLISECONDS.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    update = true;
                }
                reentrantReadWriteLock.readLock().lock();
            } finally {
                reentrantReadWriteLock.writeLock().unlock();
                // 鎖降級結束,降級爲讀鎖
            }
        }
        try {
            // 使用數據
            for (int i=0; i<5; i++) {
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":" + index);
            }
        } finally {
            reentrantReadWriteLock.readLock().unlock();
        }
    }
複製代碼


3、synchronized和Lock區別

一、synchronized和Lock區別

synchronized是關鍵字,而Lock是接口

synchronized沒法判斷是否獲取鎖的狀態,Lock能夠判斷是否獲取到鎖

synchronized自動釋放鎖(a線程執行完同步代碼會自動釋放鎖,b線程執行過程當中發生異常會釋放鎖)

lock需在finally中手動釋放鎖(unlock()方法釋放鎖),不然容易形成線程死鎖。

synchronized關鍵字的兩個線程1和線程2,若當前線程1得到鎖,線程2等待,若是線程1阻塞,線程2會一直等待下去。

而lock鎖不必定會等待下去,若是嘗試得到不到鎖,線程能夠不用一直等待就結束了。


synchronized的鎖可重入、不可中斷、非公平

lock鎖可重入,可中斷、可公平

lock鎖適合大量同步的代碼的同步問題synchronized鎖適合代碼少許的同步問題


不可重入鎖:自旋鎖、wait()、notify()、notifyAll()

不可重入鎖,即不可遞歸調用,遞歸調用會發生死鎖


二、reentrantlock和synchronized區別

reentrantLock擁有synchronized相同的併發性和內存語義,此外還多列鎖投票、定時鎖等候和中斷所等候

使用synchronized鎖,A不釋放,B將一直等待下去

使用reentrantlock鎖,A不釋放,B等待一段時間就會中斷等待,而幹別的事情。


synchronized是在JVM層面上實現的,不但能夠經過一些監控工具監控synchronized的鎖定,並且代碼執行時出現異常,JVM會走到哪一個釋放鎖定。可是Lock不行

在資源競爭不是很激烈的狀況下,synchronized的性能優於reentrantlock鎖,而競爭激烈的狀況下,synchronized的性能降低幾十倍,而reentrantlock的性能維持常態。


  • 性能分析

synchronized屢次自旋,以得到鎖,在這個過程當中等待的線程不會被掛起,於是節省了掛起和喚醒的上下文切換的開銷

reentrantlock不會自旋,而是直接掛起

於是在線程併發量不大的狀況下,synchronized由於擁有自旋鎖、偏向鎖和輕量級鎖的緣由,不用將等待線程掛起,偏向鎖甚至不用自旋,因此在這種狀況下要比reenttrantlock高效。


默認狀況下synchronized非公平鎖;reentrantlock默認是非公平鎖。


  • 綁定多個條件

一個Reentrantlock對象能夠同時綁定多個Condition對象,

而在synchronized中,鎖對象的wait()notify()notifyAll()方法能夠實現一個隱含的條件。

若是要和多餘一個添加關聯的時候,synchronized就不得不額外地添加一個鎖,而Reentrantlock則無須這麼作只須要屢次調用new Condition()方法便可。

相關文章
相關標籤/搜索