1. 程式人生 > >多執行緒之Lock與synchronized比較及使用

多執行緒之Lock與synchronized比較及使用

   第一:先比較兩者的區別:
類別 synchronized Lock
存在層次 Java的關鍵字,在jvm層面上 是一個類 java.util.concurrent.locks

鎖的釋放 1、以獲取鎖的執行緒執行完同步程式碼,釋放鎖 2、執行緒執行發生異常,jvm會讓執行緒釋放鎖 一般用在try()catch{}中然後在finally中必須釋放鎖,不然容易造成執行緒死鎖
鎖的獲取 假設A執行緒獲得鎖,B執行緒等待。如果A執行緒阻塞,B執行緒會一直等待
分情況而定,Lock有多個鎖獲取的方式 一般如果用Lock.lock()鎖那麼如果一個執行緒佔用另外一個執行緒也必須在一直等待當前執行緒釋放鎖之後才能執行 但是如果使用的是tyLock()鎖的話一個執行緒佔用鎖那麼其他執行緒則不用等待可以先去做其他事情或者返回
鎖狀態 無法判斷 可以判斷 tryLock()方法是有返回值的,它表示用來嘗試獲取鎖,如果獲取成功,則返回true,如果獲取失敗(即鎖已被其他執行緒獲取),則返回false,也就說這個方法無論如何都會立即返回。在拿不到鎖時不會一直在那等待。 tryLock(long time, TimeUnit unit)方法和tryLock()方法是類似的,只不過區別在於這個方法在拿不到鎖時會等待一定的時間,在時間期限之內如果還拿不到鎖,就返回false。如果如果一開始拿到鎖或者在等待期間內拿到了鎖,則返回true。
鎖型別 可重入 不可中斷 非公平 可重入 可判斷 可公平(兩者皆可)
效能 少量同步(併發量小的時候效率還可以) 大量同步(併發量大時效率也可以,在併發量小的時候與 synchronized區別不大
第二:使用synchronized時一定要注意:             使用synchronized時鎖標誌一定要用static修飾,代表不管多少執行緒訪問使用的都是同一個標誌             可以使用在方法體上也可以使用在程式碼塊中        例如:                   private static Object object = new Object();   //用static修飾                   private static int piaoshu = 50;//用static修飾 第三:使用類 java.util.concurrent.locks中的Lock鎖          一: 先來介紹ReentrantLock 鎖             private static ReentrantLock lock = new ReentrantLock();             使用lock鎖時一定要注意必須宣告為全域性的,如果宣告為區域性的那麼每個執行緒在使用的時候都會new一個ReentrantLock物件出來那麼每 個執行緒使用的都不是同一個ReentrantLock鎖那麼這樣加鎖就沒有任何          ReentrantLock中的方法都有哪些呢,最常用的有以下幾種:         lock()、tryLock()、tryLock(long time, TimeUnit unit),lockInterruptibly()釋放所的方法是unLock()          1.首先lock()方法是平常使用得最多的一個方法,就是用來獲取鎖。如果使用此方法進行對執行緒加鎖那麼當一個執行緒獲取鎖之後其他執行緒則需要等待這個執行緒釋放所之後才能進行作用同synchronized一樣         2.如果當一個執行緒獲取鎖之後不想讓其他執行緒等待那麼可以使用tryLock()、tryLock(long time, TimeUnit unit)這兩個方法中的任意一個加鎖他們都有返回值true或false,tryLock(long time, TimeUnit unit)方法是讓執行緒等待多久之後如果還沒有獲得鎖那麼就返回或者去做其他操作         3.他們的使用一般都是在try()catch{}中使用,並且只會對在lock.lock()和lock.unlock()中間的程式碼進行加鎖,之外的程式碼不會進行加鎖,所以當A和B兩個執行緒同時執行的時候,如果A先獲得鎖了,那麼B就沒獲得鎖,所以B是不能執行lock.unlock()之後的程式碼的,只能等A執行完之後B才能加鎖然後釋放鎖之後才能繼續執行下面的程式碼,這就是程式碼的執行順序問題,前面的程式碼沒執行完畢後面的程式碼是不能執行的(非同步和從新開一個執行緒除外)         4.lock.lock()使用方法,就是用來獲取鎖。如果鎖已被其他執行緒獲取,另外的執行緒則進行等待。   
      lock.lock();
        try{
            //處理任務
            中間加鎖執行的程式碼
        }catch(Exception ex){
            捕獲異常
        }finally{
            lock.unlock();   //釋放鎖
        }

        5.tryLock()和tryLock(long time, TimeUnit unit)的使用,其中tryLock(代表時間, 代表時間單位可以是毫秒,納秒,秒)             tryLock()方法是有返回值的,它表示用來嘗試獲取鎖,如果獲取成功,則返回true,如果獲取失敗(即鎖已被其他執行緒獲取),則返回false,也就說這個方法無論如何都會立即返回。在拿不到鎖時不會一直在那等待。 tryLock(long time, TimeUnit unit)方法和tryLock()方法是類似的,只不過區別在於這個方法在拿不到鎖時會等待一定的時間,在時間期限之內如果還拿不到鎖,就返回false。如果如果一開始拿到鎖或者在等待期間內拿到了鎖,則返回true。 所以,一般情況下通過tryLock來獲取鎖時是這樣使用的:
boolean tryLock = lock.tryLock();
或者boolean tryLock = lock.tryLock(long time, TimeUnit.MILLISECONDS);
第一個引數是時間,第二個引數是時間單位(秒或毫秒,納秒等等)
if(tryLock ) {
     try{
         //處理任務
     }catch(Exception ex){
         捕獲異常
     }finally{
         lock.unlock();   //釋放鎖
     } 
}else {
    //如果不能獲取鎖,則直接做其他事情
}

            6.其他方法                 isHeldByCurrentThread():查詢當前執行緒是否保持此鎖定                  isFair():判斷Lock是否為公平鎖                  isLocked():查詢lock 是否被任意執行緒所持有。        二:ReentrantReadWriteLock讀寫鎖實現了ReadWriteLock介面                 1. ReentrantReadWriteLock裡面提供了很多豐富的方法,不過最主要的有兩個方法:readLock()和writeLock()用來獲取讀鎖和寫鎖。                 2.如果讀寫檔案時使用synchronized鎖那麼如果兩個執行緒都是讀檔案那麼一個執行緒佔用另外一個執行緒則必須等待這樣不利於讀取效率,如果使用讀鎖則不一樣了             3.使用方法

            

public class Test {
    private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
     
    public static void main(String[] args)  {
        final Test test = new Test();
         
        new Thread(){
            public void run() {
                test.get(Thread.currentThread());
            };
        }.start();
         
        new Thread(){
            public void run() {
                test.get(Thread.currentThread());
            };
        }.start();
         
    }  
     
    public void get(Thread thread) {
        rwl.readLock().lock();
        try {
            long start = System.currentTimeMillis();
             
            while(System.currentTimeMillis() - start <= 1) {
                System.out.println(thread.getName()+"正在進行讀操作");
            }
            System.out.println(thread.getName()+"讀操作完畢");
        } finally {
            rwl.readLock().unlock();
        }
    }
}

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