08互斥鎖,讀寫鎖和條件

java.util.concurrent.locks包提供了鎖和等待條件的接口和類, 可用於替代JDK1.5之前的同步(synchronized)和監視器機制(主要是Object類的wait(), notify(), notifyAll()方法).

 

互斥鎖--Lock接口及其實現類ReentrantLock

所謂互斥鎖, 指的是一次最多只能有一個線程持有的鎖. 在jdk1.5之前, 我們通常使用synchronized機制控制多個線程對共享資源的訪問. 而現在, Lock提供了比synchronized機制更廣泛的鎖定操作, Lock和synchronized機制的主要區別:

  • synchronized機制提供了對與每個對象相關的隱式監視器鎖的訪問, 並強制所有鎖獲取和釋放均要出現在一個塊結構中, 當獲取了多個鎖時, 它們必須以相反的順序釋放. synchronized機制對鎖的釋放是隱式的, 只要線程運行的代碼超出了synchronized語句塊範圍, 鎖就會被釋放. 而Lock機制必須顯式的調用Lock對象的unlock()方法才能釋放鎖, 這爲獲取鎖和釋放鎖不出現在同一個塊結構中, 以及以更自由的順序釋放鎖提供了可能. 以下代碼演示了在不同的塊結構中獲取和釋放鎖:

Java代碼 

 收藏代碼

  1. public class LockTest {  
  2.     private static Lock lock = new ReentrantLock();  
  3.     public static void main(String[] args) {  
  4.         lock.lock();  
  5.         invokeMethod();  
  6.     }  
  7.   
  8.     private static void invokeMethod() {  
  9.         lock.unlock();  
  10.     }  
  11. }  

  爲了確保鎖被釋放, 通常會採用如下的代碼形式:

Java代碼 

 收藏代碼

  1. Lock lock = new ReentrantLock();  
  2. // 獲取鎖  
  3. lock.lock();  
  4.     try {  
  5.         // access the resource protected by this lock  
  6.     } finally {  
  7.         // 釋放鎖  
  8.         lock.unlock();  
  9.     }  

  |--void lock(): 執行此方法時, 如果鎖處於空閒狀態, 當前線程將獲取到鎖. 相反, 如果鎖已經被其他線程持有, 將禁用當前線程, 直到當前線程獲取到鎖.

|--void unlock(): 執行此方法時, 當前線程將釋放持有的鎖. 鎖只能由持有者釋放, 如果線程並不持有鎖, 卻執行該方法, 可能導致異常的發生.

  • Lock提供了一個非塊結構的獲取鎖嘗試--tryLock(), 一個獲取可中斷鎖的嘗試--lockInterruptibly()和一個獲取超時失效鎖的嘗試--tryLock(long time, TimeUnit unit).

|--boolean tryLock(): 如果鎖可用, 則獲取鎖, 並立即返回true, 否則返回false. 該方法和lock()的區別在於, tryLock()只是"試圖"獲取鎖, 如果鎖不可用, 不會導致當前線程被禁用, 當前線程仍然繼續往下執行代碼. 而lock()方法則是一定要獲取到鎖, 如果鎖不可用, 就一直等待, 在未獲得鎖之前,當前線程並不繼續向下執行. 通常採用如下的代碼形式調用tryLock()方法:

 

Java代碼 

 收藏代碼

  1. Lock lock = new ReentrantLock();  
  2.    if (lock.tryLock()) {  
  3.        try {  
  4.            // manipulate protected state  
  5.        } finally {  
  6.            lock.unlock();  
  7.        }  
  8.    } else {  
  9.        // perform alternative actions  
  10.    }  

此用法可確保如果獲取了鎖, 則會釋放鎖; 如果未獲取鎖, 則不會試圖將其釋放.

  • Lock的newCondition()方法可以獲得與該鎖綁定的Condition對象, Condition的詳細介紹如下. 

 

條件--Condition

調用Condition對象的相關方法, 可以方便的掛起和喚醒線程. Object對象的wait(), notify(), notifyAll()方法當然也可以做到這一點, 但是Object對象的這些方法存在很不方便的地方--如果多個線程調用了obj的wait()方法而掛起, 那麼我們無法做到調用obj的notify()和notifyAll()方法喚醒其中特定的一個線程. 而Condition對象就可以做到這一點. 具體的代碼請參見我的上一篇博客http://coolxing.iteye.com/blog/1236696中的解法二部分.

  • void await(): 調用Condition對象的await()方法將導致當前線程被掛起, 並釋放該Condition對象所綁定的鎖. Condition對象只能通過Lock類的newCondition()方法獲取, 因此一個Condition對象必然有一個與其綁定的Lock鎖. 調用Condition對象的await()方法的前提是: 當前線程必須持有與該Condition對象綁定的鎖, 否則程序可能拋出異常.
  • void signal(): 喚醒一個在該Condition對象上掛起的線程. 如果存在多個線程等待這個Condition對象的喚醒, 則隨機選擇一個. 線程被喚醒之前, 必須重新獲取到鎖(與該Condition對象綁定的Lock對象).
  • void signalAll(): 喚醒所有在該Condition對象上掛起的線程. 所有被喚醒的線程將競爭與該Condition對象綁定的鎖, 只有獲取到鎖的線程才能恢復到運行狀態.

 

讀寫鎖--ReadWriteLock接口及其實現類ReentrantReadWriteLock

ReentrantReadWriteLock中定義了2個內部類, ReentrantReadWriteLock.ReadLock和ReentrantReadWriteLock.WriteLock, 分別用來代表讀取鎖和寫入鎖. ReentrantReadWriteLock對象提供了readLock()和writeLock()方法, 用於獲取讀取鎖和寫入鎖. 

 

  • 讀取鎖允許多個reader線程同時持有, 而寫入鎖最多只能有一個writter線程持有.
  • 讀寫鎖的使用場合: 讀取共享數據的頻率遠大於修改共享數據的頻率. 在上述場合下, 使用讀寫鎖控制共享資源的訪問, 可以提高併發性能.
  • 如果一個線程已經持有了寫入鎖, 則可以再持有讀寫鎖. 相反, 如果一個線程已經持有了讀取鎖, 則在釋放該讀取鎖之前, 不能再持有寫入鎖.
  • 可以調用寫入鎖的newCondition()方法獲取與該寫入鎖綁定的Condition對象, 此時與普通的互斥鎖並沒有什麼區別. 但是調用讀取鎖的newCondition()方法將拋出異常. 

 

使用讀寫鎖的一個例子:

 

Java代碼 

 收藏代碼

  1. public class ReadWriteLockTest {  
  2.     private static ReadWriteLock lock = new ReentrantReadWriteLock();  
  3.     private static Person person = new Person("David Beckham", true);  
  4.   
  5.     public static void main(String[] args) {  
  6.         new Thread() {  
  7.             public void run() {  
  8.                 while(true) {  
  9.                     try {  
  10.                         lock.readLock().lock();  
  11.                         System.out.print("name = " + person.getName());  
  12.                         System.out.println(", isMan = " + person.isMan());  
  13.                     } finally {  
  14.                         lock.readLock().unlock();  
  15.                     }  
  16.                 }  
  17.             };  
  18.         }.start();  
  19.         new Thread() {  
  20.             public void run() {  
  21.                 boolean state = true;  
  22.                 while(true) {  
  23.                     try {  
  24.                         lock.writeLock().lock();  
  25.                         if (state) {  
  26.                             person.setName("Lady GaGa");  
  27.                             person.setMan(false);  
  28.                             state = false;  
  29.                         } else {  
  30.                             person.setName("David Beckham");  
  31.                             person.setMan(true);  
  32.                             state = true;  
  33.                         }  
  34.                           
  35.                     } finally {  
  36.                         lock.writeLock().unlock();  
  37.                     }  
  38.                 }  
  39.             };  
  40.         }.start();  
  41.     }  
  42. }  
  43.   
  44. class Person {  
  45.     private String name;  
  46.     private boolean isMan;  
  47.   
  48.     public Person(String name, boolean isMan) {  
  49.         this.name = name;  
  50.         this.isMan = isMan;  
  51.     }  
  52.   
  53.     public String getName() {  
  54.         return name;  
  55.     }  
  56.   
  57.     public void setName(String name) {  
  58.         this.name = name;  
  59.     }  
  60.   
  61.     public boolean isMan() {  
  62.         return isMan;  
  63.     }  
  64.   
  65.     public void setMan(boolean isMan) {  
  66.         this.isMan = isMan;  
  67.     }  
  68. }  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章