java併發編程-Lock

爲什麼有了synchronized還要有Lock

  • synchronized只有等當前線程執行完,才能釋放鎖,或者虛擬機出錯時釋放資源。html

  • synchronized可能形成其它線程長時間的等待,好比獲取鎖的線程在執行耗時的IO操做,那麼其它須要該資源的線程只能長時間的等待。java

  • synchronized沒法知道當前線程獲取鎖是否成功。緩存

Lock簡介

1:基本概念多線程

  • Lock 實現提供了比使用 synchronized 方法和語句可得到的更普遍的鎖定操做。此實現容許更靈活的結構,能夠具備差異很大的屬性,能夠支持多個相關的 Condition 對象。併發

  • 鎖是控制多個線程對共享資源進行訪問的工具。一般,鎖提供了對共享資源的獨佔訪問。一次只能有一個線程得到鎖,對共享資源的全部訪問都須要首先得到鎖。不過,某些鎖可能容許對共享資源併發訪問,如 ReadWriteLock 的讀取鎖。高併發

  • Lock 接口的實現容許鎖在不一樣的做用範圍內獲取和釋放,並容許以任何順序獲取和釋放多個鎖工具

  • 鎖定和取消鎖定出如今不一樣做用範圍中時,必須謹慎地確保保持鎖定時所執行的全部代碼用 try-finally 或 try-catch 加以保護,以確保在必要時釋放鎖。性能

  • Lock 實現提供了使用 synchronized 方法和語句所沒有的其它功能,包括提供了一個非塊結構的獲取鎖嘗試 (tryLock())、一個獲取可中斷鎖的嘗試 (lockInterruptibly()) 和一個獲取超時失效鎖的嘗試 (tryLock(long, TimeUnit))。ui

  • 全部 Lock 實現都必須 實施與內置監視器鎖提供的相同內存同步語義。spa

2:Lock接口

public interface Lock {
    void lock();    
    void lockInterruptibly() throws InterruptedException;    
    boolean tryLock();    
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;    
    void unlock();
    Condition newCondition();
}

lock()方法是日常使用得最多的一個方法,就是用來獲取鎖。若是鎖不可用,出於線程調度目的,將禁用當前線程,而且在得到鎖以前,該線程將一直處於休眠狀態。

Lock 實現可能可以檢測到鎖的錯誤使用,好比會致使死鎖的調用,在那種環境下還可能拋出一個 (unchecked) 異常。Lock 實現必須對環境和異常類型進行記錄。1

通常的格式爲:記住lock使用結束後必定要釋放。

Lock lock = ...;
lock.lock();try{
    doSomeThing();
}catch(Exception ex){

}finally{
    lock.unlock();   
}123456789

boolean tryLock()方法 
僅在調用時鎖爲空閒狀態才獲取該鎖。若是鎖可用,則獲取鎖,並當即返回值 true。若是鎖不可用,則此方法將當即返回值 false。此用法可確保若是獲取了鎖,則會釋放鎖,若是未獲取鎖,則不會試圖將其釋放。 
經常使用使用格式爲:

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

boolean tryLock(long time,TimeUnit unit)方法 
若是鎖在給定的等待時間內空閒,而且當前線程未被中斷,則獲取鎖。若是鎖可用,則此方法將當即返回值 true。若是鎖不可用,出於線程調度目的,將禁用當前線程,而且在發生如下三種狀況之一前,該線程將一直處於休眠狀態:

  • 鎖由當前線程得到

  • 其餘某個線程中斷當前線程,而且支持對鎖獲取的中斷;

  • 或已超過指定的等待時間 
    若是得到了鎖,則返回值 true。

若是當前線程:在進入此方法時已經設置了該線程的中斷狀態;或者 
在獲取鎖時被中斷,而且支持對鎖獲取的中斷,則將拋出 InterruptedException,並會清除當前線程的已中斷狀態。若是超過了指定的等待時間,則將返回值 false。若是 time 小於等於 0,該方法將徹底不等待。

void lockInterruptibly()方法 
lockInterruptibly()方法比較特殊,當經過這個方法去獲取鎖時,若是線程正在等待獲取鎖,則這個線程可以響應中斷,即中斷線程的等待狀態。也就使說,當兩個線程同時經過lock.lockInterruptibly()想獲取某個鎖時,倘若此時線程A獲取到了鎖,而線程B只有在等待,那麼對線程B調用threadB.interrupt()方法可以中斷線程B的等待過程。

  因爲lockInterruptibly()的聲明中拋出了異常,因此lock.lockInterruptibly()必須放在try塊中或者在調用lockInterruptibly()的方法外聲明拋出InterruptedException。 
  

public void method() throws InterruptedException {
    lock.lockInterruptibly();
    try {  
     doSomeThing();
    }
    finally {
        lock.unlock();
    }  }123456789
所以當經過lockInterruptibly()方法獲取某個鎖時,若是不能獲取到,只有進行等待的狀況下,是能夠響應中斷的。而用synchronized修飾的話,當一個線程處於等待某個鎖的狀態,是沒法被中斷的,只有一直等待下去。

Condition newCondition() 
返回綁定到此 Lock 實例的新 Condition 實例。 
在等待條件前,鎖必須由當前線程保持。調用 Condition.await() 將在等待前以原子方式釋放鎖,並在等待返回前從新獲取鎖。

3:ReentrantLock簡介 
基本概念:

  • 一個可重入的互斥鎖 Lock,它具備與使用 synchronized 方法和語句所訪問的隱式監視器鎖相同的一些基本行爲和語義,但功能更強大。

  • ReentrantLock 將由最近成功得到鎖,而且尚未釋放該鎖的線程所擁有。當鎖沒有被另外一個線程所擁有時,調用 lock 的線程將成功獲取該鎖並返回。若是當前線程已經擁有該鎖,此方法將當即返回。可使用 isHeldByCurrentThread() 和 getHoldCount() 方法來檢查此狀況是否發生。

  • 此類的構造方法接受一個可選的公平 參數。當設置爲 true 時,在多個線程的爭用下,這些鎖傾向於將訪問權授予等待時間最長的線程。不然此鎖將沒法保證任何特定訪問順序。

  • 使用公平鎖的衆多線程中的一員可能得到多倍的成功機會,這種狀況發生在其餘活動線程沒有被處理而且目前並未持有鎖時。還要注意的是,未定時的 tryLock 方法並無使用公平設置。由於即便其餘線程正在等待,只要該鎖是可用的,此方法就能夠得到成功。

package com.csu.thread;

import java.util.HashSet;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class ReenteantLockTest {    //static Lock lock=new ReentrantLock();
    private static HashSet<Integer> hashSet=new HashSet<>();    public static void main(String[] args) {        // TODO Auto-generated method stub
        Sum sum=new Sum();        new Thread(new Runnable() {
            @SuppressWarnings("static-access")            public void run() {
                sum.add(10000);                try {
                    Thread.currentThread().sleep(10000);
                    System.out.println("當前線程:"+Thread.currentThread().getName()+"結束");
                } catch (InterruptedException e) {                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }).start();        new Thread(new Runnable() {            public void run() {
                sum.add(20000);
                System.out.println("當前線程:"+Thread.currentThread().getName()+"結束");
            }
        }).start();

    }    public static class Sum {        private Lock lock=new ReentrantLock();        public void add(int i)
        {            lock.lock();            int sum=0;            try {
                System.out.println("當前線程:"+Thread.currentThread().getName()+"獲得了鎖");                for(int j=0;j<i;j++)
                {
                    sum+=j;
                }

            } catch (Exception e) {                // TODO: handle exception
            }            finally {                lock.unlock();
                System.out.println("當前線程:"+Thread.currentThread().getName()+"釋放鎖");
            }
            hashSet.add(sum);
        }
    }

}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162

運行結果:

當前線程:Thread-0獲得了鎖
當前線程:Thread-0釋放鎖
當前線程:Thread-1獲得了鎖
當前線程:Thread-1釋放鎖
當前線程:Thread-1結束
當前線程:Thread-0結束123456

咱們發現Thread-0沒有結束,就及時的釋放了線程獲取的資源。 
tryLock():

import java.util.HashSet;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class ReenteantLockTest {    //static Lock lock=new ReentrantLock();
    private static HashSet<Integer> hashSet=new HashSet<>();    public static void main(String[] args) {        // TODO Auto-generated method stub
        Sum sum=new Sum();        new Thread(new Runnable() {            public void run() {
                sum.add(1000000000);
                    System.out.println("當前線程:"+Thread.currentThread().getName()+"結束");

            }
        }).start();        new Thread(new Runnable() {            public void run() {
                sum.add(20000);
                System.out.println("當前線程:"+Thread.currentThread().getName()+"結束");
            }
        }).start();

    }    public static class Sum {        private Lock lock=new ReentrantLock();        public  void add(int i)
        {            if(lock.tryLock()){            int sum=0;            try {
                System.out.println("當前線程:"+Thread.currentThread().getName()+"獲得了鎖");                for(int j=0;j<i;j++)
                {
                    sum+=j;
                }

            } catch (Exception e) {                // TODO: handle exception
            }            finally {                lock.unlock();
                System.out.println("當前線程:"+Thread.currentThread().getName()+"釋放鎖");
            }
            hashSet.add(sum);
            }            else {
                System.out.println("當前線程:"+Thread.currentThread().getName()+"獲取鎖失敗");
            }
        }
    }

}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758

運行結果:

當前線程:Thread-0獲得了鎖
當前線程:Thread-1獲取鎖失敗
當前線程:Thread-1結束
當前線程:Thread-0釋放鎖
當前線程:Thread-0結束12345

發現:Thread-1獲取鎖失敗,可是它不阻塞,從結果咱們能夠發現:當前線程:Thread-1結束 打印出來就表示線程沒有阻塞。

lockInterruptibly()

package com.csu.thread;

import java.util.HashSet;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class ReenteantLockTest {    //static Lock lock=new ReentrantLock();
    private static HashSet<Integer> hashSet=new HashSet<>();    public static void main(String[] args) {        // TODO Auto-generated method stub
        Sum sum=new Sum();
        Thread t1=new Thread(new Runnable() {            public void run() {                try {
                    sum.add(1000000000);
                    System.out.println("當前線程:"+Thread.currentThread().getName()+"結束");
                } catch (InterruptedException e) {
                    System.out.println(Thread.currentThread().getName()+"被中斷");
                }

            }
        });
        Thread t2=new Thread(new Runnable() {            public void run() {                try {
                    sum.add(20000);
                    System.out.println("當前線程:"+Thread.currentThread().getName()+"結束");
                } catch (InterruptedException e) {                    // TODO Auto-generated catch block
                    System.out.println(Thread.currentThread().getName()+"被中斷");
                }

            }
        });
        t1.start();
        t2.start();
        t2.interrupt();


    }    public static class Sum {        private Lock lock=new ReentrantLock();

        @SuppressWarnings("static-access")        public  void add(int i) throws InterruptedException
        {            lock.lockInterruptibly();            int sum=0;            try {
                System.out.println("當前線程:"+Thread.currentThread().getName()+"獲得了鎖");                for(int j=0;j<i;j++)
                {
                    sum+=j;
                }
                Thread.currentThread().sleep(10000);
            } catch (Exception e) {                // TODO: handle exception
            }            finally {                lock.unlock();
                System.out.println("當前線程:"+Thread.currentThread().getName()+"釋放鎖");
            }
            hashSet.add(sum);

        }
    }   
}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071

運行結果:

當前線程:Thread-0獲得了鎖Thread-1被中斷
當前線程:Thread-0釋放鎖
當前線程:Thread-0結束1234

是的Thread-1被成功的中斷了。 
當咱們註釋掉這段代碼運行發現:t2.interrupt();

當前線程:Thread-0獲得了鎖
當前線程:Thread-0釋放鎖
當前線程:Thread-1獲得了鎖
當前線程:Thread-0結束
當前線程:Thread-1釋放鎖
當前線程:Thread-1結束123456

經過結果發現,若是咱們不中斷等待線程,則lockInterruptibly是阻塞的,它會等待獲取鎖。

4:ReadWriteLock

  • ReadWriteLock 維護了一對相關的鎖,一個用於只讀操做,另外一個用於寫入操做。只要沒有 writer,讀取鎖能夠由多個 reader 線程同時保持。寫入鎖是獨佔的。

  • 全部 ReadWriteLock 實現都必須保證 writeLock 操做的內存同步效果也要保持與相關 readLock 的聯繫。也就是說,成功獲取讀鎖的線程會看到寫入鎖以前版本所作的全部更新。

  • 讀-寫鎖容許對共享數據進行更高級別的併發訪問。

  • 儘管讀-寫鎖的基本操做是直截了當的,但實現仍然必須做出許多決策,這些決策可能會影響給定應用程序中讀-寫鎖的效果。這些策略的例子包括: 
    1:在 writer 釋放寫入鎖時,reader 和 writer 都處於等待狀態,在這時要肯定是授予讀取鎖仍是授予寫入鎖。Writer 優先比較廣泛,由於預期寫入所需的時間較短而且不那麼頻。 
    2:在 reader 處於活動狀態而 writer 處於等待狀態時,肯定是否向請求讀取鎖的 reader 授予讀取鎖。Reader 優先會無限期地延遲 writer,而 writer 優先會減小可能的併發。 
    3:肯定是否從新進入鎖:可使用帶有寫入鎖的線程從新獲取它嗎?能夠在保持寫入鎖的同時獲取讀取鎖嗎?能夠從新進入寫入鎖自己嗎? 
    4:能夠將寫入鎖在不容許其餘 writer 干涉的狀況降低級爲讀取鎖嗎?能夠優先於其餘等待的 reader 或 writer 將讀取鎖升級爲寫入鎖嗎?

ReadWriteLock也是一個接口,在它裏面只定義了兩個方法:

public interface ReadWriteLock {
    /**
     * Returns the lock used for reading.
     *
     * @return the lock used for reading.
     */
    Lock readLock();    /**
     * Returns the lock used for writing.
     *
     * @return the lock used for writing.
     */
    Lock writeLock();
}123456789101112131415

一個用來獲取讀鎖,一個用來獲取寫鎖。也就是說將文件的讀寫操做分開,分紅2個鎖來分配給線程,從而使得多個線程能夠同時進行讀操做。下面的ReentrantReadWriteLock實現了ReadWriteLock接口。 
5:ReentrantReadWriteLock

  • 此類不會將讀取者優先或寫入者優先強加給鎖訪問的排序。可是,它確實支持可選的公平 策略。

  • 此鎖容許 reader 和 writer 按照 ReentrantLock 的樣式從新獲取讀取鎖或寫入鎖。在寫入線程保持的全部寫入鎖都已經釋放後,才容許重入 reader 使用它們。

  • writer 能夠獲取讀取鎖,但反過來則不成立。

  • 重入還容許從寫入鎖降級爲讀取鎖,其實現方式是:先獲取寫入鎖,而後獲取讀取鎖,最後釋放寫入鎖。可是,從讀取鎖升級到寫入鎖是不可能的。

  • 讀取鎖和寫入鎖都支持鎖獲取期間的中斷。

  • 寫入鎖提供了一個 Condition 實現,對於寫入鎖來講,該實現的行爲與 ReentrantLock.newCondition() 提供的 Condition 實現對 ReentrantLock 所作的行爲相同。固然,此 Condition 只能用於寫入鎖。

  • 讀取鎖不支持 Condition,readLock().newCondition() 會拋出 UnsupportedOperationException。 
    實例1:下面的代碼展現瞭如何利用重入來執行升級緩存後的鎖降級

class CachedData {
   Object data;
   volatile boolean cacheValid;
   ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

   void processCachedData() {
     rwl.readLock().lock();
     if (!cacheValid) {
        // Must release read lock before acquiring write lock
        rwl.readLock().unlock();
        rwl.writeLock().lock();
        // Recheck state because another thread might have acquired
        //   write lock and changed state before we did.        if (!cacheValid) {
          data = ...
          cacheValid = true;
        }
        // Downgrade by acquiring read lock before releasing write lock
        rwl.readLock().lock();
        rwl.writeLock().unlock(); // Unlock write, still hold read
     }

     use(data);
     rwl.readLock().unlock();
   }
 }1234567891011121314151617181920212223242526

實例二:在使用某些種類的 Collection 時,可使用 ReentrantReadWriteLock 來提升併發性。

class RWDictionary {    private final Map<String, Data> m = new TreeMap<String, Data>();    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();    private final Lock r = rwl.readLock();    private final Lock w = rwl.writeLock();    public Data get(String key) {
        r.lock();        try { return m.get(key); }        finally { r.unlock(); }
    }    public String[] allKeys() {
        r.lock();        try { return m.keySet().toArray(); }        finally { r.unlock(); }
    }    public Data put(String key, Data value) {
        w.lock();        try { return m.put(key, value); }        finally { w.unlock(); }
    }    public void clear() {
        w.lock();        try { m.clear(); }        finally { w.unlock(); }
    }
 }123456789101112131415161718192021222324252627

一般,在預期 collection 很大,讀取者線程訪問它的次數多於寫入者線程,而且 entail 操做的開銷高於同步開銷時,這很值得一試。 
實例三:大量讀取鎖 
1:用同步關鍵字

public class ReadWriterLockTest {    public static void main(String[] args)  {
        final ReadWriterLockTest test = new ReadWriterLockTest();        new Thread(){            public void run() {
                test.get(Thread.currentThread());
            };
        }.start();        new Thread(){            public void run() {
                test.get(Thread.currentThread());
            };
        }.start();

    }  

    public synchronized void get(Thread thread) {        long start = System.currentTimeMillis();        while(System.currentTimeMillis() - start <= 1) {
            System.out.println(thread.getName()+"正在進行讀操做");
        }
        System.out.println(thread.getName()+"讀操做完畢");
    }

}1234567891011121314151617181920212223242526272829
Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0讀操做完畢Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1讀操做完畢123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129

咱們發現,直到thread1執行完讀操做以後,纔會打印thread2執行讀操做的信息。 
2:改成ReentrantReadWriteLock

package com.csu.thread;

import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriterLockTest {    private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();    public static void main(String[] args)  {
        final ReadWriterLockTest test = new ReadWriterLockTest();        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()+"讀操做完畢");

        } catch (Exception e) {            // TODO: handle exception
        }finally
        {
            rwl.readLock().unlock();
        }

    }

}12345678910111213141516171819202122232425262728293031323334353637383940414243

運行結果:

Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-0正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-0正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-0讀操做完畢Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1正在進行讀操做Thread-1讀操做完畢12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061

說明thread1和thread2在同時進行讀操做。這樣就大大提高了讀操做的效率。不過要注意的是,若是有一個線程已經佔用了讀鎖,則此時其餘線程若是要申請寫鎖,則申請寫鎖的線程會一直等待釋放讀鎖。 
若是有一個線程已經佔用了寫鎖,則此時其餘線程若是申請寫鎖或者讀鎖,則申請的線程會一直等待釋放寫鎖。

6.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。因此說,在具體使用時要根據適當狀況選擇。 
   
7:鎖的相關概念介紹 
1.可重入鎖

  若是鎖具有可重入性,則稱做爲可重入鎖。像synchronized和ReentrantLock都是可重入鎖,可重入性在我看來實際上代表了鎖的分配機制:基於線程的分配,而不是基於方法調用的分配。舉個簡單的例子,當一個線程執行到某個synchronized方法時,好比說method1,而在method1中會調用另一個synchronized方法method2,此時線程沒必要從新去申請鎖,而是能夠直接執行方法method2。 
  

class MyClass {    public synchronized void method1() {
        method2();
    }    public synchronized void method2() {

    }
}123456789

上述代碼中的兩個方法method1和method2都用synchronized修飾了,假如某一時刻,線程A執行到了method1,此時線程A獲取了這個對象的鎖,而因爲method2也是synchronized方法,假如synchronized不具有可重入性,此時線程A須要從新申請鎖。可是這就會形成一個問題,由於線程A已經持有了該對象的鎖,而又在申請獲取該對象的鎖,這樣就會線程A一直等待永遠不會獲取到的鎖。

  而因爲synchronized和Lock都具有可重入性,因此不會發生上述現象。 
   
2.可中斷鎖

  可中斷鎖:顧名思義,就是能夠相應中斷的鎖。 
  在Java中,synchronized就不是可中斷鎖,而Lock是可中斷鎖。 
  若是某一線程A正在執行鎖中的代碼,另外一線程B正在等待獲取該鎖,可能因爲等待時間過長,線程B不想等待了,想先處理其餘事情,咱們可讓它中斷本身或者在別的線程中中斷它,這種就是可中斷鎖。 
  在前面演示lockInterruptibly()的用法時已經體現了Lock的可中斷性。 
   
3.公平鎖

 公平鎖即儘可能以請求鎖的順序來獲取鎖。好比同是有多個線程在等待一個鎖,當這個鎖被釋放時,等待時間最久的線程(最早請求的線程)會得到該所,這種就是公平鎖。

   非公平鎖即沒法保證鎖的獲取是按照請求鎖的順序進行的。這樣就可能致使某個或者一些線程永遠獲取不到鎖。 
  在Java中,synchronized就是非公平鎖,它沒法保證等待的線程獲取鎖的順序。 
  而對於ReentrantLock和ReentrantReadWriteLock,它默認狀況下是非公平鎖,可是能夠設置爲公平鎖。 
   
4.讀寫鎖

  讀寫鎖將對一個資源(好比文件)的訪問分紅了2個鎖,一個讀鎖和一個寫鎖。 
  正由於有了讀寫鎖,才使得多個線程之間的讀操做不會發生衝突。 
  ReadWriteLock就是讀寫鎖,它是一個接口,ReentrantReadWriteLock實現了這個接口。 
  能夠經過readLock()獲取讀鎖,經過writeLock()獲取寫鎖。 
  上面已經演示過了讀寫鎖的使用方法,在此再也不贅述。 
  

引用塊內容 
http://www.cnblogs.com/dolphin0520/p/3923167.html java核心技術 卷I

相關文章
相關標籤/搜索