java併發編程--互斥鎖, 讀寫鎖及條件

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

 

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

所謂互斥鎖, 指的是一次最多隻能有一個線程持有的鎖. 在jdk1.5以前, 咱們一般使用synchronized機制控制多個線程對共享資源的訪問. 而如今, Lock提供了比synchronized機制更普遍的鎖定操做, Lock和synchronized機制的主要區別:高併發

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

Java代碼 性能

  1. public class LockTest {  
        private static Lock lock = new ReentrantLock();  
        public static void main(String[] args) {  
            lock.lock();  
            invokeMethod();  
        }  
      
        private static void invokeMethod() {  
            lock.unlock();  
        }  
    }  

     

  爲了確保鎖被釋放, 一般會採用以下的代碼形式:this

Java代碼 線程

  1. Lock lock = new ReentrantLock();  
    // 獲取鎖  
    lock.lock();  
        try {  
            // access the resource protected by this lock  
        } finally {  
            // 釋放鎖  
            lock.unlock();  
        }  

     

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

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

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

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

 

Java代碼 blog

  1. Lock lock = new ReentrantLock();  
       if (lock.tryLock()) {  
           try {  
               // manipulate protected state  
           } finally {  
               lock.unlock();  
           }  
       } else {  
           // perform alternative actions  
       }  

     

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

  • 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代碼 

public class ReadWriteLockTest {  
    private static ReadWriteLock lock = new ReentrantReadWriteLock();  
    private static Person person = new Person("David Beckham", true);  
  
    public static void main(String[] args) {  
        new Thread() {  
            public void run() {  
                while(true) {  
                    try {  
                        lock.readLock().lock();  
                        System.out.print("name = " + person.getName());  
                        System.out.println(", isMan = " + person.isMan());  
                    } finally {  
                        lock.readLock().unlock();  
                    }  
                }  
            };  
        }.start();  
        new Thread() {  
            public void run() {  
                boolean state = true;  
                while(true) {  
                    try {  
                        lock.writeLock().lock();  
                        if (state) {  
                            person.setName("Lady GaGa");  
                            person.setMan(false);  
                            state = false;  
                        } else {  
                            person.setName("David Beckham");  
                            person.setMan(true);  
                            state = true;  
                        }  
                          
                    } finally {  
                        lock.writeLock().unlock();  
                    }  
                }  
            };  
        }.start();  
    }  
}  
  
class Person {  
    private String name;  
    private boolean isMan;  
  
    public Person(String name, boolean isMan) {  
        this.name = name;  
        this.isMan = isMan;  
    }  
  
    public String getName() {  
        return name;  
    }  
  
    public void setName(String name) {  
        this.name = name;  
    }  
  
    public boolean isMan() {  
        return isMan;  
    }  
  
    public void setMan(boolean isMan) {  
        this.isMan = isMan;  
    }  
}  

無論程序運行多久, 也不會有人妖的出現.

相關文章
相關標籤/搜索