什麼是structured lock?先來段代碼:java
public synchronized boolean contains(final Integer object) {
Entry pred = this.head;
Entry curr = pred.next;
while (curr.object.compareTo(object) < 0) {
pred = curr;
curr = curr.next;
}
return object.equals(curr.object);
}
這段代碼用synchronized來解決併發問題,這個例子是在方法上上鎖,也就是object級別,那麼一旦這個object被上鎖,該object的全部同步方法都會被鎖,鎖被釋放的時機是方法執行完畢,提到synchronized,也順便提一下wait/notify好了,看如下代碼:c++
public class Main { public static void main(String[] args){ ThreadB b = new ThreadB(); b.start(); synchronized(b){ try{ System.out.println("Waiting for b to complete..."); b.wait(); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("Total is: " + b.total); } } } class ThreadB extends Thread{ int total; @Override public void run(){ synchronized(this){ try { sleep(2000); } catch (InterruptedException e) { System.out.println("wokao"); } for(int i=0; i<100 ; i++){ total += i; } notify(); } } }
(這個程序通常運行不會有問題,但有個潛在的bug,誰能看出來)結合這個例子,兩句話基本能解釋併發
wait()
tells the calling thread to give up the monitor and go to sleep until some other thread enters the same monitor and calls notify( ).notify()
wakes up the first thread that called wait()
on the same object.這種synchronized對同步機制,又稱爲structured lock,由於看起來很結構化,很規整有沒有?ide
可是結果化對應對結果就是不夠靈活。何時須要靈活呢?舉個例子this
有這麼個鏈表A->B->C->D->E->Fspa
假設有以下一組工做:線程
1.寫A和B;code
2.寫B和C對象
3.寫C和Dblog
4.寫D和E
5.寫E和F
若是用synchronized把1中的A和B鎖了,那麼2就必須等A和B都執行完成才能,執行,可是有必要嗎?其實只要等A結束,2就應該能夠執行了,依次往下,這就是所謂的hand in hand locking,
就是連環鎖,描述以下:
lock A and B, start job 1
unlock A, lock C, start job 2
unlock B, lock D, start job 3 ...
這種連環鎖,顯然synchronized是作不到的,sychronized裏面能夠套其餘的synchronized,那隻能造成一個nested結構。
這就是synchronized或者說structured lock的侷限性。
那麼這種狀況下 unstructured lock就有用了,看一個使用的例子:
public static final class CoarseList extends ListSet { /* * TODO Declare a lock for this class to be used in implementing the * concurrent add, remove, and contains methods below. */ ReentrantLock lk= new ReentrantLock(); /** * Default constructor. */ public CoarseList() { super(); } /** * {@inheritDoc} * * TODO Use a lock to protect against concurrent access. */ @Override boolean add(final Integer object) { try{ lk.lock(); Entry pred = this.head; Entry curr = pred.next; while (curr.object.compareTo(object) < 0) { pred = curr; curr = curr.next; } if (object.equals(curr.object)) { return false; } else { final Entry entry = new Entry(object); entry.next = curr; pred.next = entry; return true; } }finally { lk.unlock(); } }
這裏的ReentrantLock就很靈活,不過必須顯式地unlock,不然會出問題(就像c++必須顯示delete內存同樣),爲了防止意外例如拋出exception,應該把unlock語句放在finally裏,因此這裏的弊端想必也能看到了吧,處處都要寫try finally語句!!!
unstructured lock還有個好處能夠區分讀寫鎖,理論上多個線程讀不會有問題,因此用一個比較弱的讀寫鎖便可,而一旦有一個線程寫,其餘線程就要注意了,最多隻能有一個線程在被鎖對象上寫,此時其餘的線程不管讀仍是寫,都得等。上例子:
public static final class RWCoarseList extends ListSet { /* * TODO Declare a read-write lock for this class to be used in * implementing the concurrent add, remove, and contains methods below. */ /** * Default constructor. */ public RWCoarseList() { super(); } /** * {@inheritDoc} * * TODO Use a read-write lock to protect against concurrent access. */ ReentrantReadWriteLock rwlk = new ReentrantReadWriteLock(); @Override boolean add(final Integer object) { try { rwlk.writeLock().lock(); Entry pred = this.head; Entry curr = pred.next; while (curr.object.compareTo(object) < 0) { pred = curr; curr = curr.next; } if (object.equals(curr.object)) { return false; } else { final Entry entry = new Entry(object); entry.next = curr; pred.next = entry; return true; } }finally { rwlk.writeLock().unlock(); } }
還沒完,unstructured lock的第三個好處是,支持trylock,看名字就能夠看出來,就是先嚐試拿鎖,拿不到,作其餘事去。想一想synchronized是怎麼作的,先嚐試拿鎖,拿不到,block!高下立判