synchronized只有等當前線程執行完,才能釋放鎖,或者虛擬機出錯時釋放資源。html
synchronized可能形成其它線程長時間的等待,好比獲取鎖的線程在執行耗時的IO操做,那麼其它須要該資源的線程只能長時間的等待。java
synchronized沒法知道當前線程獲取鎖是否成功。緩存
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