標籤:sts calling 記憶體 remove cal head 代碼 err 方法
什麼是structured lock?先來段代碼:
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好了,看以下代碼:
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,因為看起來很結構化,很規整有沒有?
但是結果化對應對結果就是不夠靈活。什麼時候需要靈活呢?舉個例子
有這麼個鏈表A->B->C->D->E->F
假設有如下一組工作:
1.寫A和B;
2.寫B和C
3.寫C和D
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!高下立判
Java structured lock vs unstructured lock