<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级在线观看免费| 一级毛片免费毛片毛片| 免费A级毛片无码A∨| 国产亚洲综合一区柠檬导航| 一本岛v免费不卡一二三区| 亚洲精品高清在线| 一个人看的www免费高清 | 69堂人成无码免费视频果冻传媒| 亚洲成av人影院| 久久免费福利视频| 亚洲视频手机在线| 成年女人喷潮毛片免费播放| 亚洲.国产.欧美一区二区三区| 免费一看一级毛片| 中文字幕不卡免费视频| 亚洲国产人成在线观看69网站| 又大又硬又爽又粗又快的视频免费| 亚洲最新黄色网址| 精品久久久久久久免费人妻| 免费看一级高潮毛片| 亚洲熟妇av一区二区三区| 无码日韩精品一区二区免费暖暖| 亚洲喷奶水中文字幕电影| 午夜爱爱免费视频| aaa毛片免费观看| 亚洲精品国产福利在线观看| 午夜视频在线在免费| 精品一区二区三区免费观看| 久久狠狠高潮亚洲精品| 日韩免费视频观看| 久久精品国产免费一区| 亚洲成_人网站图片| 狠狠综合久久综合88亚洲| 最近新韩国日本免费观看| 亚洲av无码专区在线电影天堂| 久久精品国产亚洲精品| 免费看黄视频网站| 一区二区三区免费视频网站| 亚洲小说区图片区| 亚洲尤码不卡AV麻豆| 无人在线观看完整免费版视频 |