<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    letter Y A N. G Brass Letter F a n-spo D Pewter Uppercase Letter I N G
    隨筆 - 4, 文章 - 10, 評論 - 2, 引用 - 0
    數據加載中……

    再談ReentrantLock

    入鎖(ReentrantLock)是一種遞歸無阻塞的同步機制。以前一直認為它是synchronized的簡單替代,而且實現機制也不相差太遠。不過最近實踐過程中發現它們之間還是有著天壤之別。

    以下是官方說明:一個可重入的互斥鎖定 Lock,它具有與使用 synchronized 方法和語句所訪問的隱式監視器鎖定相同的一些基本行為和語義,但功能更強大。ReentrantLock 將由最近成功獲得鎖定,并且還沒有釋放該鎖定的線程所擁有。當鎖定沒有被另一個線程所擁有時,調用 lock 的線程將成功獲取該鎖定并返回。如果當前線程已經擁有該鎖定,此方法將立即返回。可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法來檢查此情況是否發生。

    它提供了lock()方法:
    如果該鎖定沒有被另一個線程保持,則獲取該鎖定并立即返回,將鎖定的保持計數設置為 1。
    如果當前線程已經保持該鎖定,則將保持計數加 1,并且該方法立即返回。
    如果該鎖定被另一個線程保持,則出于線程調度的目的,禁用當前線程,并且在獲得鎖定之前,該線程將一直處于休眠狀態,此時鎖定保持計數被設置為 1。

    最近在研究Java concurrent中關于任務調度的實現時,讀了延遲隊列DelayQueue的一些代碼,比如take()。該方法的主要功能是從優先隊列(PriorityQueue)取出一個最應該執行的任務(最優值),如果該任務的預訂執行時間未到,則需要wait這段時間差。反之,如果時間到了,則返回該任務。而offer()方法是將一個任務添加到該隊列中。

    后來產生了一個疑問:如果最應該執行的任務是一個小時后執行的,而此時需要提交一個10秒后執行的任務,會出現什么狀況?還是先看看take()的源代碼:

    public E take() throws InterruptedException {

    final ReentrantLock lock = this.lock;

    lock.lockInterruptibly();

    try {

    for (;;) {

    E first
    = q.peek();

    if (first == null) {

    available.await();

    }
    else {

    long delay = first.getDelay(TimeUnit.NANOSECONDS);

    if (delay > 0) {

    long tl = available.awaitNanos(delay);

    }
    else {

    E x
    = q.poll();

    assert x != null;

    if (q.size() != 0)

    available.signalAll();
    // wake up other takers

    return x;

    }

    }

    }

    }
    finally {

    lock.unlock();

    }

    }

    而以下是offer()的源代碼:

    public boolean offer(E e) {

    final ReentrantLock lock = this.lock;

    lock.lock();

    try {

    E first
    = q.peek();

    q.offer(e);

    if (first == null || e.compareTo(first) < 0)

    available.signalAll();

    return true;

    }
    finally {

    lock.unlock();

    }

    }

    如代碼所示,take()和offer()都是lock了重入鎖。如果按照synchronized的思維(使用諸如synchronized(obj)的方法),這兩個方法是互斥的。回到剛才的疑問,take()方法需要等待1個小時才能返回,而offer()需要馬上提交一個10秒后運行的任務,會不會一直等待take()返回后才能提交呢?答案是否定的,通過編寫驗證代碼也說明了這一點。這讓我對重入鎖有了更大的興趣,它確實是一個無阻塞的鎖。

    下面的代碼也許能說明問題:運行了4個線程,每一次運行前打印lock的當前狀態。運行后都要等待5秒鐘。

    public static void main(String[] args) throws InterruptedException {

    final ExecutorService exec = Executors.newFixedThreadPool(4);

    final ReentrantLock lock = new ReentrantLock();

    final Condition con = lock.newCondition();

    final int time = 5;

    final Runnable add = new Runnable() {

    public void run() {

    System.out.println(
    "Pre " + lock);

    lock.lock();

    try {

    con.await(time, TimeUnit.SECONDS);

    }
    catch (InterruptedException e) {

    e.printStackTrace();

    }
    finally {

    System.out.println(
    "Post " + lock.toString());

    lock.unlock();

    }

    }

    };

    for(int index = 0; index < 4; index++)

    exec.submit(add);

    exec.shutdown();

    }

     

    這是它的輸出:
    Pre ReentrantLock@a59698[Unlocked]
    Pre ReentrantLock@a59698[Unlocked]
    Pre ReentrantLock@a59698[Unlocked]
    Pre ReentrantLock@a59698[Unlocked]
    Post ReentrantLock@a59698[Locked by thread pool-1-thread-1]
    Post ReentrantLock@a59698[Locked by thread pool-1-thread-2]
    Post ReentrantLock@a59698[Locked by thread pool-1-thread-3]
    Post ReentrantLock@a59698[Locked by thread pool-1-thread-4]

    每一個線程的鎖狀態都是“Unlocked”,所以都可以運行。但在把con.await改成Thread.sleep(5000)時,輸出就變成了:
    Pre ReentrantLock@a59698[Unlocked]
    Pre ReentrantLock@a59698[Locked by thread pool-1-thread-1]
    Pre ReentrantLock@a59698[Locked by thread pool-1-thread-1]
    Pre ReentrantLock@a59698[Locked by thread pool-1-thread-1]
    Post ReentrantLock@a59698[Locked by thread pool-1-thread-1]
    Post ReentrantLock@a59698[Locked by thread pool-1-thread-2]
    Post ReentrantLock@a59698[Locked by thread pool-1-thread-3]
    Post ReentrantLock@a59698[Locked by thread pool-1-thread-4]

    以上的對比說明線程在等待時(con.await),已經不在擁有(keep)該鎖了,所以其他線程就可以獲得重入鎖了。

    有必要會過頭再看看Java官方的解釋:“如果該鎖定被另一個線程保持,則出于線程調度的目的,禁用當前線程,并且在獲得鎖定之前,該線程將一直處于休眠狀態”。我對這里的“保持”的理解是指非wait狀態外的所有狀態,比如線程Sleep、for循環等一切有CPU參與的活動。一旦線程進入wait狀態后,它就不再keep這個鎖了,其他線程就可以獲得該鎖;當該線程被喚醒(觸發信號或者timeout)后,就接著執行,會重新“保持”鎖,當然前提依然是其他線程已經不再“保持”了該重入鎖。

    總結一句話:對于重入鎖而言,"lock"和"keep"是兩個不同的概念。lock了鎖,不一定keep鎖,但keep了鎖一定已經lock了鎖。

    posted on 2008-10-03 17:55 rainman 閱讀(3963) 評論(0)  編輯  收藏 所屬分類: java多線程

    主站蜘蛛池模板: 亚洲精品无码aⅴ中文字幕蜜桃| 亚洲精品无码AV人在线播放| 亚洲精品456在线播放| 免费在线黄色网址| 亚洲最大成人网色香蕉| 中文字幕免费高清视频| 亚洲男人的天堂在线播放| 男人都懂www深夜免费网站| 国产亚洲综合一区柠檬导航| 国产区在线免费观看| 亚洲中文字幕久久精品无码APP | 久久久久亚洲AV成人网人人网站| 亚洲免费观看视频| 国产免费久久久久久无码| 亚洲色精品vr一区二区三区| 97在线视频免费公开视频| 亚洲国产精品国自产电影| 亚洲成人免费在线观看| 国产精品亚洲美女久久久| 黄页视频在线观看免费| 久久亚洲中文字幕精品一区| 国产精品偷伦视频观看免费 | 日韩免费福利视频| 久久久久亚洲AV片无码| 免费无码午夜福利片69| 国产精品亚洲美女久久久| 一级毛片aaaaaa免费看| 亚洲国产91在线| 成年女人永久免费观看片| fc2免费人成在线视频| 久久丫精品国产亚洲av不卡| 在线观看H网址免费入口| 麻豆亚洲AV成人无码久久精品| 成人福利免费视频| 亚洲AV日韩AV一区二区三曲| 久久亚洲国产成人影院网站 | 噼里啪啦免费观看高清动漫4| 亚洲gay片在线gv网站| 亚洲精品卡2卡3卡4卡5卡区| 日本免费一区二区在线观看| WWW亚洲色大成网络.COM|