鎖是併發編程中的重要概念,用來控制多個線程對同一資源的併發訪問,在支持併發的編程語言中都有體現,好比c++ python等。本文主要講解Java中的鎖,或者說是重入鎖。之因此這麼說是由於在Java中,鎖主要就是指重入鎖。 java中的鎖分爲兩大類:一種是synchronized內置鎖,另外一種是顯式的Lock鎖。在Java中,Lock接口的主要實現是重入鎖ReentrantLock,而內置鎖也是可重入鎖。html
這兩種鎖的簡單比較以下:java
synchronized內置鎖和ReentrantLock都是可重入鎖。 synchronized就不是可中斷鎖,而Lock是可中斷鎖。 synchronized 和Lock都是不公平鎖。 Lock能夠設置爲公平鎖。 node
在nts 生產代碼中,並無使用顯式Lock,而是大量地使用了synchronized關鍵字。本文主要包括這兩種鎖的實現、主要的方法和使用。其中在講解使用時,會結合在jdk中的使用以及在nts中的使用等方式來說解。python
outline:
1 java 的內置鎖(synchronized關鍵字)的使用
2 ReentrantLock的實現、主要方法和使用c++
3 Condition的實現、主要方法和使用編程
4 ReentrantReadWriteLock的實現、主要方法和使用併發
1 java 的內置鎖(synchronized關鍵字)的使用:
synchronized 關鍵字有兩種用法:synchronized 方法和 synchronized 塊。其中synchronized方法又分爲靜態方法和實例方法兩種。synchronized塊又能夠分爲普通對象塊,this對象塊和class對象塊。因爲能夠針對任意代碼塊,且可任意指定上鎖的對象,故synchronized塊的靈活性較高。app
當一個線程獲取了對應的內置鎖,並執行該代碼塊時,其餘線程便只能一直等待獲取鎖的線程釋放鎖,而獲取鎖的線程只會在兩種狀況下釋放鎖:
1)獲取鎖的線程執行完了該代碼塊,而後釋放對鎖的佔有;
2)線程執行發生異常,此時JVM會讓線程自動釋放鎖。
1.1 synchronized 方法:框架
1 // in push.Latch 2 public synchronized void enter(final long timeout) throws InterruptedException //經過在方法聲明中加入 synchronized關鍵字來聲明 synchronized 方法 3 { 4 num++; 5 if (num < total) 6 { 7 if (0L < timeout) 8 { 9 wait(timeout); // here InterruptedException may be thrown 10 } 11 } 12 else 13 { 14 notifyAll(); // must in a synchronized method 15 } 16 }
1.2 static synchronized 方法less
static synchronized方法是對class的類對象加鎖,synchronized方法是對class的當前object加鎖。
1 // in dca.access.IconStore 2 public static synchronized IconStore getInstance() // 性能較差的singleton模式 3 { 4 try 5 { 6 if (null == instance) 7 { 8 instance = new IconStore(); 9 } 10 } 11 catch (final NotesException e) 12 { 13 XLog.error("Could not create icon store object", e); 14 } 15 16 return instance; 17 }
1.3 普通對象上的synchronized 塊:
1 //nts.util.Events
private static final Map<String, SignalValues> events = new HashMap<String, SignalValues>()
... 2 public Object clone() 3 { 4 Events s = null; 5 6 try 7 { 8 synchronized (events) // on plain object 9 { 10 s = (Events) super.clone(); 11 } 12 } 13 catch (final CloneNotSupportedException e) 14 { 15 XLog.error(e); 16 } 17 return s; 18 }
1.4 類對象上的synchronized 塊
1 private static TravelerSocketFactory getInstance() // in util.TravelerSocketFactory 2 { 3 if (instance == null) 4 { 5 synchronized (TravelerSocketFactory.class) // double-checking lock 6 { 7 if (instance == null) 8 { 9 instance = new TravelerSocketFactory(); 10 } 11 } 12 } 13 return instance; 14 }
1.5 this 對象上synchronized 塊
1 // in push.Latch 2 public boolean start(final Object obj) 3 { 4 final long timeStart = System.currentTimeMillis(); 5 boolean rv = false; 6 Barrier b = null; 7 synchronized (this) // 當前對象 8 { 9 if (!latched.containsKey(obj)) 10 { 11 b = new Barrier("Latch:" + name + "_Obj:" + obj, 1); 12 latched.put(obj, b); 13 rv = true; 14 } 15 } 16 XLog.exiting("name=" + name, "obj=" + obj, "barrier=" + b, "rv=" + rv, ("Elapsed time=" 17 + (System.currentTimeMillis() - timeStart) + "ms")); 18 return rv; 19 }
1.6 Java內置鎖的可重入性
下面這段code能夠證實對象關聯monitor上的鎖是重入鎖。若是鎖具有可重入性,則稱做爲可重入鎖。一個線程不能獲取其餘線程所擁有的鎖,可是它能夠獲取它已經擁有的鎖。 容許一個線程屢次獲取同一個鎖,使得重入同步成爲可能。考慮如下場景:同步代碼(位於同步塊或者同步方法中的代碼)直接或者間接地調用某個方法,而該方法 一樣包含同步代碼(上述兩組代碼使用一樣的鎖)。若是沒有重入同步,同步代碼將不得不使用額外的預警機制來避免一個線程由於本身阻塞本身而死鎖。可重入性 在我看來實際上代表了鎖的分配機制:基於線程的分配,而不是基於方法調用的分配。
1 class Test { 2 public static void main(String[] args) { 3 Test t = new Test(); 4 synchronized(t) { 5 synchronized(t) { 6 System.out.println("made it!"); 7 } 8 } 9 } 10 }
程序的輸出結果爲:
1 made it!
在JICP中舉了一個例子,是兩個synchronized方法。其中一個調用了另一個。若是不是可重入鎖,則會死鎖。
2 jdk中的顯式Lock
java.util.concurrent包是java5的一個重大改進,java.util.concurrent包提供了多種線程間同步和通訊的機制,好比Executors, Queues, Timing, Synchronizers和Concurrent Collections等。其中Synchronizers包含了五種:Semaphore信號量,CounDownLatch倒計時鎖存器,CyclicBarrier循環柵欄,Phaser和Exchanger。 另外java.util.concurrent包還包含了兩個子包:java.util.concurrent.Atomics和java.util.concurrent.Locks。
2.1 Lock接口的方法及使用
Lock接口一共定義瞭如下6個不一樣的方法:其中lock()、lockInterruptibly(), TryLock()、和tryLock(long time, TimeUnit unit))是用來獲取鎖的。unLock()方法是用來釋放鎖的。newCondition() 用來生成新的Condition對象。(跟Lock用來替代Synchronized相似,Condition用來替代object monitor上的wait()/notify()方法。) 下面分別來介紹這些方法的使用:
2.1.1 void lock()
Lock接口獲取鎖的方式有4中,首先lock()方法是日常使用得最多的一個方法,就是用來獲取鎖。若是鎖已被其餘線程獲取,則進行等待。(跟synchronized同樣,可使用synchronized代替lock。)
1 // java.util.concurrent.CyclicBarrier 2 public boolean isBroken() { 3 final ReentrantLock lock = this.lock; // this.lock is a feild in CyclicBarrier, this is a optimize method which is heavily used by doug lea 4 lock.lock(); 5 try { 6 return generation.broken; 7 } finally { 8 lock.unlock(); 9 } 10 }
lock()的不正確使用方法示例:
1 public class Test { 2 private ArrayList<Integer> arrayList = new ArrayList<Integer>(); 3 public static void main(String[] args) { 4 final Test test = new Test(); 5 6 new Thread(){ 7 public void run() { 8 test.insert(Thread.currentThread()); 9 }; 10 }.start(); 11 12 new Thread(){ 13 public void run() { 14 test.insert(Thread.currentThread()); 15 }; 16 }.start(); 17 } 18 19 public void insert(Thread thread) { 20 Lock lock = new ReentrantLock(); //注意這個地方 21 lock.lock(); 22 try { 23 System.out.println(thread.getName()+"獲得了鎖"); 24 for(int i=0;i<5;i++) { 25 arrayList.add(i); 26 } 27 } catch (Exception e) { 28 // TODO: handle exception 29 }finally { 30 System.out.println(thread.getName()+"釋放了鎖"); 31 lock.unlock(); 32 } 33 } 34 }
2.1.2 boolean tryLock()方法
在當前時間(被調用時)查詢鎖是否可用。若是不可用則當即返回。
1 // java.util.concurrent.ForkJoinTask 2 static final void helpExpungeStaleExceptions() { 3 final ReentrantLock lock = exceptionTableLock; 4 if (lock.tryLock()) { 5 try { 6 expungeStaleExceptions(); 7 } finally { 8 lock.unlock(); 9 } 10 } 11 }
2.1.3 void lockInterruptibly() throws InterruptedException;
這個方法涉及到一個概念,可中斷鎖。顧名思義,就是能夠響應中斷的鎖。若是某一線程A正在執行鎖中的代碼,另外一線程B正在等待獲取該鎖,可能因爲等待時間過長,線程B不想等待了,想先處理其餘事情,咱們可讓它中斷本身或者在別的線程中中斷它,這種就是可中斷鎖。單獨調用interrupt()方法不能中斷正在運行過程當中的線程,只能中斷阻塞過程當中的線程。用synchronized修飾的話,當一個線程處於等待 某個鎖的狀態,是沒法被中斷的,只有一直等待下去。Lock可讓等待鎖的線程響應中斷,而synchronized卻不行,使用 synchronized時,等待的線程會一直等待下去,不可以響應中斷(在threadA中調用threadB.interrupt();)。
// java.util.concurrent.ArrayBlockingQueue public void put(E e) throws InterruptedException { checkNotNull(e); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); // can be interrupted here try { while (count == items.length) notFull.await(); // notFull is a Condition of lock. enqueue(e); } finally { lock.unlock(); } }
2.1.4 boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
在jdk和nts生產代碼中沒有該方法的使用。
2.1.5 Condition newCondition();
用來返回一個Condition接口的對象,Condition對象的使用見下一節。
2.1.6 void unlock();
採用synchronized不須要用戶去手動釋放 鎖,當synchronized方法或者 synchronized代碼塊執行完以後,系統會自動讓線程釋放對鎖的佔用;而Lock則必需要用戶去手動釋放鎖,若是沒有主動釋放鎖,就有可能致使出 現死鎖現象。 synchronized在發生異常時,會自動釋放線程佔有的鎖,所以不會致使死鎖現象發生;而Lock在發生異常時,若是沒有主動經過unLock() 去釋放鎖,則極可能形成死鎖現象,所以使用Lock時須要在finally塊中釋放鎖。
1 public boolean addAll(Collection<? extends E> c) { // java.util.concurrent.CopyOnWriteArrayList 2 Object[] cs = (c.getClass() == CopyOnWriteArrayList.class) ? 3 ((CopyOnWriteArrayList<?>)c).getArray() : c.toArray(); 4 if (cs.length == 0) 5 return false; 6 final ReentrantLock lock = this.lock; 7 lock.lock(); 8 try { 9 Object[] elements = getArray(); 10 int len = elements.length; 11 if (len == 0 && cs.getClass() == Object[].class) 12 setArray(cs); 13 else { 14 Object[] newElements = Arrays.copyOf(elements, len + cs.length); 15 System.arraycopy(cs, 0, newElements, len, cs.length); 16 setArray(newElements); 17 } 18 return true; 19 } finally { 20 lock.unlock(); 21 } 22 }
2.2 ReentrantLock的實現:
ReentrantLock內部有一個Sync內部類,它是抽象類AQS(abstractQueuedSychronizer)的子類,它又有兩個子類NonFairSync和FairSync。 ReentrantLock的上述方法都是經過Sync內部類來實現的。
newCondition():可見ReentrantLock的實現基本徹底基於AQS,ReentrantLock返回的Condition對象,是它的AQS內部類的內部類ConditionObject的實例(在下一節會詳述):
1 public Condition newCondition() { 2 return sync.newCondition(); 3 }
lock(): 包裹了sync內部類的lock方法。
1 public void lock() { 2 sync.lock(); 3 }
tryLock():
1 public boolean tryLock(long timeout, TimeUnit unit) 2 throws InterruptedException { 3 return sync.tryAcquireNanos(1, unit.toNanos(timeout)); 4 }
lockInterruptibly():
1 public void lockInterruptibly() throws InterruptedException { 2 sync.acquireInterruptibly(1); 3 }
unLock():
1 public void unlock() { 2 sync.release(1); 3 }
下面再來看一下ReentrantLock如何實現公平鎖和非公平鎖(公平鎖:當持有該鎖的線程釋放掉公平鎖的時候,等待該鎖的其餘線程得到該鎖的機會均等,不存在某個線程屢次得到該鎖,而其餘某個線程一直處在飢餓狀態的狀況。)
FairSync:
1 protected final boolean tryAcquire(int acquires) { 2 final Thread current = Thread.currentThread(); 3 int c = getState(); 4 if (c == 0) { 5 if (!hasQueuedPredecessors() && // 若是處於等待隊列的最前面 6 compareAndSetState(0, acquires)) { 7 setExclusiveOwnerThread(current); 8 return true; 9 } 10 } 11 else if (current == getExclusiveOwnerThread()) { 12 int nextc = c + acquires; 13 if (nextc < 0) 14 throw new Error("Maximum lock count exceeded"); 15 setState(nextc); 16 return true; 17 } 18 return false; 19 } 20 }
NonFairSync():
1 final boolean nonfairTryAcquire(int acquires) { 2 final Thread current = Thread.currentThread(); 3 int c = getState(); 4 if (c == 0) { 5 if (compareAndSetState(0, acquires)) { // 直接開搶,誰搶着誰執行 6 setExclusiveOwnerThread(current); 7 return true; 8 } 9 } 10 else if (current == getExclusiveOwnerThread()) { 11 int nextc = c + acquires; 12 if (nextc < 0) // overflow 13 throw new Error("Maximum lock count exceeded"); 14 setState(nextc); 15 return true; 16 } 17 return false; 18 }
公平鎖和非公平鎖能夠經過ReentrantLock的構造函數來實現,默認構造函數用來構造NonFairLock, 而帶參數的public ReentrantLock(boolean fair) 方法能夠選擇構造NonFairLock仍是FairLock。
1 public ReentrantLock() { 2 sync = new NonfairSync(); 3 }
帶參數構造函數:
1 public ReentrantLock(boolean fair) { 2 sync = fair ? new FairSync() : new NonfairSync(); 3 }
ReentrantLock 還提供了幾個查詢方法:getHoldCount,isHoldByCurrentThread,isLocked, isFair, getOwner, hasQueuedThreads, getQueueLength, 等等,再也不贅述。
3 Condition的實現、主要方法和使用
Lock與Condition的組合,能夠用來替代Synchronized&wait/notify的組合。
3.1 實現
如前所述,Condition的實現也離不開AQS。
返回函數:
1 public Condition newCondition() { // in java.util.concurrent.ReentrantLock 2 return sync.newCondition(); 3 }
sync是什麼呢? 在 ReentrantLock類中定義了一個抽象class Sync(抽象類AQS的子類),它又有兩個子類FairSync和NonFairSync。上面代碼中的sync就是NonFairSync的對象。
1 abstract static class Sync extends AbstractQueuedSynchronizer { 2 ... 3 final ConditionObject newCondition() { 4 return new ConditionObject(); 5 } 6 ... 7 }
在java中實現了 Conditon接口的類只有兩 個:AbstractQueuedSynchronizer.ConditionObject和 AbstractLongQueuedSynchronized.ConditionObject。
3.2 方法
Condition接口經過提供下列方法,提供了與Object.notify()/Object.notifyAll()相似的功能:
1 //能夠被中斷,無期限等待。 2 void await() throws InterruptedException; 3 //無期限等待,不可中斷。 4 void awaitUninterruptibly(); 5 //等待特定時間,超時則返回;可中斷。返回值是返回時距離超時所剩時間。 6 long awaitNanos(long nanosTimeout) throws InterruptedException; 7 //等待特定時間,超時則返回;可中斷。超時返回,返回值爲false。 8 boolean await(long time, TimeUnit unit) throws InterruptedException; 9 //等待到特定時間點,超時則返回;可中斷。超時返回,返回值爲false。 10 boolean awaitUntil(Date deadline) throws InterruptedException; 11 void signal(); 12 void signalAll();
3.3 CyclicBarrier中的使用
下面以java.util.concurrent包下的CyclicBarrier和BlockingQueue(接口)爲例,來看一下這些方法的使用,首先看一下CyclicBarrier中的:
NewCondition():
1 // in CyclicBarrier 2 /** The lock for guarding barrier entry */ 3 private final ReentrantLock lock = new ReentrantLock(); 4 /** Condition to wait on until tripped */ 5 private final Condition trip = lock.newCondition();
signalAll():
1 private void nextGeneration() { 3 // signal completion of last generation 4 trip.signalAll(); 5 // set up next generation 6 count = parties; 7 generation = new Generation(); 8 }
await()&awaitNanos(long):
1 private int dowait(boolean timed, long nanos) // CyclicBarrier 2 throws InterruptedException, BrokenBarrierException, 3 TimeoutException { 4 final ReentrantLock lock = this.lock; 5 lock.lock(); 6 try { 7 final Generation g = generation; 8 9 if (g.broken) 10 throw new BrokenBarrierException(); 11 12 if (Thread.interrupted()) { 13 breakBarrier(); 14 throw new InterruptedException(); 15 } 16 17 int index = --count; // count is how many thread will be barriered 18 if (index == 0) { // tripped 19 boolean ranAction = false; 20 try { 21 final Runnable command = barrierCommand; 22 if (command != null) 23 command.run(); 24 ranAction = true; 25 nextGeneration(); 26 return 0; 27 } finally { 28 if (!ranAction) 29 breakBarrier(); 30 } 31 } 32 33 // loop until tripped, broken, interrupted, or timed out 34 for (;;) { 35 try { 36 if (!timed) 37 trip.await(); 38 else if (nanos > 0L) 39 nanos = trip.awaitNanos(nanos); 40 } catch (InterruptedException ie) { 41 if (g == generation && ! g.broken) { 42 breakBarrier(); 43 throw ie; 44 } else { 45 // We're about to finish waiting even if we had not 46 // been interrupted, so this interrupt is deemed to 47 // "belong" to subsequent execution. 48 Thread.currentThread().interrupt(); 49 } 50 } 51 52 if (g.broken) 53 throw new BrokenBarrierException(); 54 55 if (g != generation) 56 return index; 57 58 if (timed && nanos <= 0L) { 59 breakBarrier(); 60 throw new TimeoutException(); 61 } 62 } 63 } finally { 64 lock.unlock(); 65 } 66 }
3.4 ArrayBlockingQueue中的使用:
newCondition():
能夠爲一個重入鎖設置多個Condition對象
1 public ArrayBlockingQueue(int capacity, boolean fair) { 2 if (capacity <= 0) 3 throw new IllegalArgumentException(); 4 this.items = new Object[capacity]; 5 lock = new ReentrantLock(fair); 6 notEmpty = lock.newCondition(); 7 notFull = lock.newCondition(); 8 }
signal():
1 public void put(E e) throws InterruptedException { 2 checkNotNull(e); 3 final ReentrantLock lock = this.lock; 4 lock.lockInterruptibly(); 5 try { 6 while (count == items.length) 7 notFull.await(); 8 enqueue(e); 9 } finally { 10 lock.unlock(); 11 } 12 }
3.5 LinkedBlockingQueue中的使用
new:
1 /** Lock held by put, offer, etc */ 2 private final ReentrantLock putLock = new ReentrantLock(); 3 4 /** Wait queue for waiting puts */ 5 private final Condition notFull = putLock.newCondition();
signal():
1 public void put(E e) throws InterruptedException { 2 if (e == null) throw new NullPointerException(); 3 // Note: convention in all put/take/etc is to preset local var 4 // holding count negative to indicate failure unless set. 5 int c = -1; 6 Node<E> node = new Node<E>(e); 7 final ReentrantLock putLock = this.putLock; 8 final AtomicInteger count = this.count; 9 putLock.lockInterruptibly(); 10 try { 11 while (count.get() == capacity) { 12 notFull.await(); 13 } 14 enqueue(node); 15 c = count.getAndIncrement(); 16 if (c + 1 < capacity) 17 notFull.signal(); 18 } finally { 19 putLock.unlock(); 20 } 21 if (c == 0) 22 signalNotEmpty(); 23 }
4 讀寫鎖 ReadWriteLock 接口
ReadWriteLock接口只提供兩個方法定義,其中readLock()方法返回一個ReentrantLock.ReadLock類的對象做爲 readLock,另外一個writeLock()方法返回一個ReentrantLock.WriteLock類的對象做爲writeLock。
當沒有寫線程時,readLock能夠被多個讀線程同時持有(互斥鎖:在任一時刻只容許一個線程持有,其餘線程將被阻塞。 與之對應的是共享鎖(S鎖,share)又稱讀鎖。)。而寫鎖則是互斥的: 一些鎖容許對共享資源的併發訪問,好比一個readWriteLock中的讀鎖。
若是有一個線程已經佔用了讀鎖,則此時其餘線程若是要申請寫鎖,則申請寫鎖的線程會一直等待釋放讀鎖。
若是有一個線程已經佔用了寫鎖,則此時其餘線程若是申請寫鎖或者讀鎖,則申請的線程會一直等待釋放寫鎖。
4.1 示例:
1 import java.util.concurrent.*; 2 import java.util.Date; 3 import java.util.concurrent.locks.*; 4 import java.text.*; 5 6 public class ReentrantReadWriteLockTest { 7 private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); 8 private SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss"); 9 private Lock readLock = readWriteLock.readLock(); 10 private Lock writeLock = readWriteLock.writeLock(); 11 12 public void read() { 13 readLock.lock(); 14 try { 15 System.out.println(format.format(new Date()) + "---read---"); 16 TimeUnit.SECONDS.sleep(3); 17 } catch (InterruptedException e) { 18 e.printStackTrace(); 19 } finally { 20 readLock.unlock(); 21 } 22 } 23 24 public void write() { 25 writeLock.lock(); 26 try { 27 System.out.println(format.format(new Date()) + "---write---"); 28 TimeUnit.SECONDS.sleep(3); 29 } catch (InterruptedException e) { 30 e.printStackTrace(); 31 } finally { 32 writeLock.unlock(); 33 } 34 } 35 36 public static class MyThread extends Thread { 37 private ReentrantReadWriteLockTest reentrantReadWriteLockTest; 38 private String methodName; 39 40 public MyThread(ReentrantReadWriteLockTest reentrantReadWriteLockTest, String methodName) { 41 super(); 42 this.reentrantReadWriteLockTest = reentrantReadWriteLockTest; 43 this.methodName = methodName; 44 } 45 46 @Override 47 public void run() { 48 if ("read".equalsIgnoreCase(methodName)) 49 reentrantReadWriteLockTest.read(); 50 else 51 reentrantReadWriteLockTest.write(); 52 } 53 } 54 55 public static void main(String[] args) { 56 ReentrantReadWriteLockTest test = new ReentrantReadWriteLockTest(); 57 Thread t1 = new MyThread(test, "write"); // replace write with read and check what will happen? 58 Thread t2 = new MyThread(test, "read"); 59 t1.start(); 60 t2.start(); 61 } 62 }
4.2 使用
讀寫鎖的典型應用場景爲對容器的讀寫。可是,java的容器框架提供了各類各樣的免鎖或併發容器,好比最簡單的併發容器Collections.synchronizedMap()等等。
在免鎖容器中,CopyOnWriteArrayList對讀操做不會block,對寫操做會block;ConcurrentHashMap 類將 Map 的存儲空間分爲若干塊,每塊擁有本身的鎖,大大減小了多個線程爭奪同一個鎖的狀況: 對讀操做不會block 即便是對寫操做,咱們也不須要對整個Map對象加鎖,從而避免在整個對象上block。所以讀寫鎖的應用並非特別普遍。
下面來看一下jdk中使用ReentantreadWriteLock的例子javax.swing.plaf.nimbus.ImageCache :
1 // Lock for concurrent access to map 2 private ReadWriteLock lock = new ReentrantReadWriteLock();
flush:
爲何flush方法加讀鎖?
1 public void flush() { 2 lock.readLock().lock(); 3 try { 4 map.clear(); 5 } finally { 6 lock.readLock().unlock(); 7 } 8 }
getImage:
1 public Image getImage(GraphicsConfiguration config, int w, int h, Object... args) { 2 lock.readLock().lock(); 3 try { 4 PixelCountSoftReference ref = map.get(hash(config, w, h, args)); 5 // check reference has not been lost and the key truly matches, in case of false positive hash match 6 if (ref != null && ref.equals(config,w, h, args)) { 7 return ref.get(); 8 } else { 9 return null; 10 } 11 } finally { 12 lock.readLock().unlock(); 13 } 14 }
setImage:
1 public boolean setImage(Image image, GraphicsConfiguration config, int w, int h, Object... args) { 2 if (!isImageCachable(w, h)) return false; 3 int hash = hash(config, w, h, args); 4 lock.writeLock().lock(); 5 try { 6 //... 7 } finally { 8 lock.writeLock().unlock(); 9 } 10 }
問題: 與ConcurrentHashMap相比,讀寫鎖的優點在哪裏?
4.3 ReentrantReadWireteLock的實現:
內部一樣是定義了NonFair和Fair的synchronizer,一樣是繼承自AQS。
5 進一步問題:
自旋鎖:
基於他這種原理,等待的時候,並不釋放cpu時間片,相比synchronized wait()操做,減少了釋放,從新獲取的消耗。 該自旋鎖適用於,當前線程競爭不強烈的時候使用。
進一步解釋synchronized和顯式Lock的區別:
在性能上來講,若是競爭資源不激烈,二者的性能是差很少的,而當競爭資源很是激烈時(即有大量線程同時競爭),此時Lock的性能要遠遠優於synchronized。 ??
6 參考文獻:
http://www.cnblogs.com/dolphin0520/p/3923167.html
http://blog.csdn.net/HEYUTAO007/article/details/6422412