1. 程式人生 > >Java高階特性—鎖

Java高階特性—鎖

1).synchronized
  加同步格式:
    synchronized( 需要一個任意的物件(鎖) ){
      程式碼塊中放操作共享資料的程式碼。
    }
  synchronized的缺陷
   synchronized是java中的一個關鍵字,也就是說是Java語言內建的特性。
     如果一個程式碼塊被synchronized修飾了,當一個執行緒獲取了對應的鎖,並執行該程式碼塊時,其他執行緒便只能一直等待,
     等待獲取鎖的執行緒釋放鎖,而這裡獲取鎖的執行緒釋放鎖只會有兩種情況:
    1)獲取鎖的執行緒執行完了該程式碼塊,然後執行緒釋放對鎖的佔有;
    2)執行緒執行發生異常,此時JVM會讓執行緒自動釋放鎖。


2).Lock
  lock和synchronized的區別
   1).Lock不是Java語言內建的,synchronized是Java語言的關鍵字,因此是內建特性。Lock是一個類,通過這個類可以實現同步訪問;
   2).Lock和synchronized有一點非常大的不同,採用synchronized不需要使用者去手動釋放鎖,
    當synchronized方法或者synchronized程式碼塊執行完之後,系統會自動讓執行緒釋放對鎖的佔用;
    而Lock則必須要使用者去手動釋放鎖,如果沒有主動釋放鎖,就有可能導致出現死鎖現象。
  3).java.util.concurrent.locks包下常用的類,lock是一個介面
  4).Lock介面中每個方法的使用:
    lock()、tryLock()、tryLock(long time, TimeUnit unit)、lockInterruptibly()是用來獲取鎖的。
    unLock()方法是用來釋放鎖的
  5).四個獲取鎖方法的區別:
    lock()方法是平常使用得最多的一個方法,就是用來獲取鎖。如果鎖已被其他執行緒獲取,則進行等待。

  由於在前面講到如果採用Lock,必須主動去釋放鎖,並且在發生異常時,不會自動釋放鎖。因此一般來說,
  使用Lock必須在try{}catch{}塊中進行,並且將釋放鎖的操作放在finally塊中進行,以保證鎖一定被被釋放,
  防止死鎖的發生。

  tryLock()方法是有返回值的,它表示用來嘗試獲取鎖,如果獲取成功,則返回true,如果獲取
  失敗(即鎖已被其他執行緒獲取),則返回false,也就說這個方法無論如何都會立即返回。在拿不到鎖時不會一直在那等待。

   tryLock(long time, TimeUnit unit)方法和tryLock()方法是類似的,只不過區別在於這個方法在拿不到鎖
  時會等待一定的時間,在時間期限之內如果還拿不到鎖,就返回false。如果如果一開始拿到鎖或者在等待期間
  內拿到了鎖,則返回true。

   lockInterruptibly()方法比較特殊,當通過這個方法去獲取鎖時,如果執行緒正在等待獲取鎖,則這個執行緒能
  夠響應中斷,即中斷執行緒的等待狀態。也就使說,當兩個執行緒同時通過lock.lockInterruptibly()想獲取某個鎖時
  ,假若此時執行緒A獲取到了鎖,而執行緒B只有等待,那麼對執行緒B呼叫threadB.interrupt()方法能夠中斷執行緒B的
  等待過程。

   注意,當一個執行緒獲取了鎖之後,是不會被interrupt()方法中斷的。
   因此當通過lockInterruptibly()方法獲取某個鎖時,如果不能獲取到,只有進行等待的情況下,是可以響應中斷的。
   而用synchronized修飾的話,當一個執行緒處於等待某個鎖的狀態,是無法被中斷的,只有一直等待下去。

  ReentrantLock
  直接使用lock介面的話,我們需要實現很多方法,不太方便,ReentrantLock是唯一實現了Lock介面的類,
  並且ReentrantLock提供了更多的方法,ReentrantLock,意思是“可重入鎖”。

  一個用來獲取讀鎖,一個用來獲取寫鎖。也就是說將檔案的讀寫操作分開,分成2個鎖來分配給執行緒,從而使得多個
  執行緒可以同時進行讀操作。下面的ReentrantReadWriteLock實現了ReadWriteLock介面。

  ReentrantReadWriteLock
   ReentrantReadWriteLock裡面提供了很多豐富的方法,不過最主要的有兩個方法:readLock()和writeLock()用
  來獲取讀鎖和寫鎖。

  注意:
     不過要注意的是,如果有一個執行緒已經佔用了讀鎖,則此時其他執行緒如果要申請寫鎖,則申請寫鎖的執行緒會一
    直等待釋放讀鎖。
  如果有一個執行緒已經佔用了寫鎖,則此時其他執行緒如果申請寫鎖或者讀鎖,則申請的執行緒會一直等待釋放寫鎖。


3).Lock和synchronized的選擇  
  1)Lock是一個介面,而synchronized是Java中的關鍵字,synchronized是內建的語言實現;
  2)synchronized在發生異常時,會自動釋放執行緒佔有的鎖,因此不會導致死鎖現象發生;而Lock在發生異常時,
如果沒有主動通過unLock()去釋放鎖,則很可能造成死鎖現象,因此使用Lock時需要在finally塊中釋放鎖;
  3)Lock可以讓等待鎖的執行緒響應中斷,而synchronized卻不行,使用synchronized時,等待的執行緒會一直等待下去,
不能夠響應中斷;
  4)通過Lock可以知道有沒有成功獲取鎖,而synchronized卻無法辦到。
  5)Lock可以提高多個執行緒進行讀操作的效率。
   在效能上來說,如果競爭資源不激烈,兩者的效能是差不多的,而當競爭資源非常激烈時(即有大量執行緒同時競爭),
此時Lock的效能要遠遠優於synchronized。所以說,在具體使用時要根據適當情況選擇