1. 程式人生 > >JUC同步鎖(五)

JUC同步鎖(五)

根據鎖的新增到Java中的時間,Java中的鎖,可以分為“同步鎖”“JUC包中的鎖”

一、同步鎖--synchronized關鍵字

通過synchronized關鍵字來進行同步,實現對競爭資源的互斥訪問的鎖。

同步鎖的原理是,對於每一個物件,有且僅有一個同步鎖;不同的執行緒能共同訪問該同步鎖。但是,在同一個時間點,該同步鎖能且只能被一個執行緒獲取到。這樣,獲取到同步鎖的執行緒就能進行CPU排程,從而在CPU上執行;而沒有獲取到同步鎖的執行緒,必須進行等待,直到獲取到同步鎖之後才能繼續執行。這就是,多執行緒通過同步鎖進行同步的原理!

二、JUC包中的鎖

相比同步鎖,JUC包中的鎖的功能更加強大,它為鎖提供了一個框架,該框架允許更靈活地使用鎖

,只是它的用法更難罷了。Lock確保當一個執行緒位於程式碼的臨界區時,另一個執行緒不進入臨界區,相對於synchronized,Lock介面及其實現類提供了更加強大、靈活的鎖機制。

JUC包中的鎖,包括:

  • Lock介面
  • ReadWriteLock介面
  • Condition介面
  • ReentrantLock獨佔鎖
  • ReentrantReadWriteLock讀寫鎖
  • CountDownLatch
  • CyclicBarrier
  • Semaphore
  • AbstractOwnableSynchronizer抽象類
  • AbstractQueuedSynchronizer抽象類
  • AbstractQueuedLongSynchronizer抽象類

下面以簡單的例項對兩種鎖進行介紹:

(1)簡單的鎖synchronized:

public class ThreadTest {
    public void test(){
        synchronized(this){
            //do something
        }
    }
}

(2)lock鎖實現

public class ThreadTest {
    Lock lock = new Lock();
    public void test(){
        // 當前執行緒會被阻塞,直到該Lock物件的unlock()方法被呼叫
        lock.lock();
        //do something
        // 釋放鎖
        lock.unlock();
    }
}

lock()方法會對Lock例項物件進行加鎖,因此所有對該物件呼叫lock()方法的執行緒都會被阻塞,直到該Lock物件的unlock()方法被呼叫。

Lock鎖實現:

public final synchronized void lock() throws InterruptedException{
    // 當isLocked為true時,呼叫lock()的執行緒在wait()呼叫上阻塞等待。
    while(this.locked) {
        this.wait();
    }
    // 讓其它正在呼叫lock()方法的執行緒能夠在Lock例項上加鎖。
    this.locked = true;
}

public final synchronized void unlock() {
    this.locked = false;
    this.notifyAll();
}

​ 當isLocked為true時,呼叫lock()的執行緒在wait()呼叫上阻塞等待。為防止該執行緒沒有收到notify()呼叫也從wait()中返回,這個執行緒會重新去檢查isLocked條件以決定當前是否可以安全地繼續執行還是需要重新保持等待,而不是認為執行緒被喚醒了就可以安全地繼續執行了。如果isLocked為false,當前執行緒會退出while(isLocked)迴圈,並將isLocked設回true,讓其它正在呼叫lock()方法的執行緒能夠在Lock例項上加鎖。

三、詳解JUC包中的各個鎖

鎖的框架圖如下:

img

(1)Lock 介面

Lock為介面型別,Lock實現提供了比使synchronized方法和語句可獲得的更廣泛的鎖定操作。此實現允許更靈活的結構,可以具有差別很大的屬性,可以支援多個相關的Condition物件。

JUC 包中的 Lock 介面支援那些語義不同 (重入、公平等) 的鎖規則。所謂語義不同,是指鎖可是有 “公平機制的鎖”、”非公平機制的鎖”、”可重入的鎖” 等等。”公平機制” 是指 “不同執行緒獲取鎖的機制是公平的”,而 “非公平機制” 則是指 “不同執行緒獲取鎖的機制是非公平的”,”可重入的鎖” 是指同一個鎖能夠被一個執行緒多次獲取。

(2)ReadWriteLock

​ ReadWriteLock為介面型別, 維護了一對相關的鎖,一個用於只讀操作,另一個用於寫入操作。只要沒有 writer,讀取鎖可以由多個 reader 執行緒同時保持。寫入鎖是獨佔的。

​ ReadWriteLock 介面以和 Lock 類似的方式定義了一些讀取者可以共享而寫入者獨佔的鎖。JUC 包只有一個類實現了該介面,即 ReentrantReadWriteLock,因為它適用於大部分的標準用法上下文。但程式設計師可以建立自己的、適用於非標準要求的實現。

(3)Condition

​ Condition為介面型別,它將 Object 監視器方法(wait、notify 和 notifyAll)分解成截然不同的物件,以便通過將這些物件與任意 Lock 實現組合使用,為每個物件提供多個等待 set (wait-set)。其中,Lock 替代了 synchronized 方法和語句的使用,Condition 替代了 Object 監視器方法的使用。可以通過await(),signal()來休眠/喚醒執行緒。

​ Condition 需要和 Lock 聯合使用,它的作用是代替 Object 監視器方法,可以通過 await(),signal() 來休眠 / 喚醒執行緒。Condition 介面描述了可能會與鎖有關聯的條件變數。這些變數在用法上與使用 Object.wait 訪問的隱式監視器類似,但提供了更強大的功能。需要特別指出的是,單個 Lock 可能與多個 Condition 物件關聯。為了避免相容性問題,Condition 方法的名稱與對應的 Object 版本中的不同。

(4)LockSupport

​ LockSupport 提供 “建立鎖” 和“其他同步類的基本執行緒阻塞原語”。
  LockSupport 的功能和 “Thread 中的 Thread.suspend()和 Thread.resume()有點類似”,LockSupport 中的 park() 和 unpark() 的作用分別是阻塞執行緒和解除阻塞執行緒。但是 park()和 unpark()不會遇到 “Thread.suspend 和 Thread.resume 所可能引發的死鎖” 問題。

(5)CountDownLatch

​ CountDownLatch為常用類,它是一個同步輔助類,在完成一組正在其他執行緒中執行的操作之前,它允許一個或多個執行緒一直等待。

(6)ReentrantLock

Reentrant:可重入

​ ReentrantLock 是獨佔鎖。所謂獨佔鎖,是指只能被獨自佔領,即同一個時間點只能被一個執行緒鎖獲取到的鎖。ReentrantLock 鎖包括 “公平的 ReentrantLock” 和 “非公平的 ReentrantLock”。”公平的 ReentrantLock” 是指 “不同執行緒獲取鎖的機制是公平的”,而 “非公平的  ReentrantLock” 則是指 “不同執行緒獲取鎖的機制是非公平的”,ReentrantLock 是 “可重入的鎖”。

參考連結

本片文章,主要整理自網際網路,便於自己複習知識所用,以下為參考連結!

【1】Java併發程式設計實戰-----“J.U.C”:鎖,lock

【2】JUC包中的鎖

【3】java多執行緒系列三:JUC鎖

【4】JUC鎖框架綜述