国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

并發(fā)學習筆記 (5)

meteor199 / 2939人閱讀

摘要:執(zhí)行會重新將設(shè)置為,并且通知喚醒其中一個若有的話在方法中調(diào)用了函數(shù)而處于等待狀態(tài)的線程。除此之外,我們需要記錄同一個線程重復對一個鎖對象加鎖的次數(shù)。競爭失敗的線程處于就緒狀態(tài),長期競爭失敗的線程就會饑餓。

tutorials site

Locks in java
  

Locks (and other more advanced synchronization mechanisms) are created using synchronized blocks, so it is not like we can get totally rid of the synchronized keyword.
鎖的實現(xiàn)是利用synchonized, wait(),notify()方法實現(xiàn)的。所以不可以認為鎖可以完全脫離synchonized實現(xiàn)。

Java包 JUC java.util.concurrent.locks 包括了很多l(xiāng)ock接口的實現(xiàn)了類,這些類足夠使用。
但是需要知道如何使用它們,以及這些類背后的理論。JUC包教程

用synchonized:可以保證在同一時間只有一個線程可以執(zhí)行 return ++count:

public class Counter{
  private int count = 0;

  public int inc(){
    synchronized(this){
      return ++count;
    }
  }
}

以下的Counter類用Lock代替synchronized 達到同樣的目的:
lock() 方法會對 Lock 實例對象進行加鎖,因此所有其他對該對象調(diào)用 lock() 方法的線程都會被阻塞,直到該 Lock 對象的 unlock() 方法被調(diào)用。

public class Counter{
  private Lock lock = new Lock();
  private int count = 0;

  public int inc(){
    lock.lock();
    int newCount = ++count;
    lock.unlock();
    return newCount;
  }
}

那么問題來了, Lock類是怎么設(shè)計的?

Lock 類的設(shè)計

一個Lock類的簡單實現(xiàn):

javapublic class Lock{
  private boolean isLocked = false;

  public synchronized void lock() throws InterruptedException{
    while(isLocked){
      wait();
    }
    isLocked = true;
  }

  public synchronized void unlock(){
    isLocked = false;
    notify();
  }
}

while(isLocked) 循環(huán), 又被稱為spin lock自旋鎖。當 isLockedtrue 時,調(diào)用 lock() 的線程在 wait() 調(diào)用上阻塞等待。為防止該線程沒有收到 notify() 調(diào)用也從 wait() 中返回(也稱作虛假喚醒),這個線程會重新去檢查 isLocked 條件以決定當前是否可以安全地繼續(xù)執(zhí)行還是需要重新保持等待,而不是認為線程被喚醒了就可以安全地繼續(xù)執(zhí)行了。如果 isLocked 為 false,當前線程會退出 while(isLocked) 循環(huán),并將 isLocked 設(shè)回 true,讓其它正在調(diào)用 lock() 方法的線程能夠在 Lock 實例上加鎖。

當線程完成了臨界區(qū)(位于 lock() 和 unlock() 之間)中的代碼,就會調(diào)用 unlock()。執(zhí)行 unlock() 會重新將 isLocked 設(shè)置為 false,并且通知(喚醒)其中一個(若有的話)在 lock() 方法中調(diào)用了 wait() 函數(shù)而處于等待狀態(tài)的線程。

鎖的可重入性

synchronized 同步塊是可重入的。這意味著: 如果一個java線程進入了代碼中的同步塊synchonzied block,并因此獲得了該同步塊使用的同步對象對應(yīng)的管程monitor object上的鎖那么這個線程可以進入由同一個管程對象所同步的另一個 java 代碼塊

前面的Lock的設(shè)計就不是可重入的:

javapublic class Reentrant2{
    Lock lock = new Lock();

    public outer(){
        lock.lock();
        inner();
        lock.unlock();
    }

    public synchronized inner(){
        lock.lock();
        //do something
        lock.unlock();
    }
}

一個線程是否被允許退出 lock() 方法是由 while 循環(huán)(自旋鎖)中的條件決定的。當前的判斷條件是只有當 isLocked 為 false 時 lock 操作才被允許,而沒有考慮是哪個線程鎖住了它。
所以需要對Lock的設(shè)計做出如下修改,才能可重入。

javapublic class Lock{
    boolean isLocked = false;
    Thread  lockedBy = null;
    int lockedCount = 0;

    public synchronized void lock()
        throws InterruptedException{
        Thread callingThread =
            Thread.currentThread();
        while(isLocked && lockedBy != callingThread){
            wait();
        }
        isLocked = true;
        lockedCount++;
        lockedBy = callingThread;
  }

    public synchronized void unlock(){
        if(Thread.curentThread() ==
            this.lockedBy){
            lockedCount--;

            if(lockedCount == 0){
                isLocked = false;
                notify();
            }
        }
    }

    ...
}

注意到現(xiàn)在的 while 循環(huán)(自旋鎖)也考慮到了已鎖住該 Lock 實例的線程。如果當前的鎖對象沒有被加鎖 (isLocked = false),或者當前調(diào)用線程已經(jīng)對該 Lock 實例加了鎖,那么 while 循環(huán)就不會被執(zhí)行,調(diào)用 lock() 的線程就可以退出該方法(譯者注:“被允許退出該方法” 在當前語義下就是指不會調(diào)用 wait() 而導致阻塞)。

除此之外,我們需要記錄同一個線程重復對一個鎖對象加鎖的次數(shù)。否則,一次 unblock() 調(diào)用就會解除整個鎖,即使當前鎖已經(jīng)被加鎖過多次。在 unlock() 調(diào)用沒有達到對應(yīng) lock() 調(diào)用的次數(shù)之前,我們不希望鎖被解除。

現(xiàn)在這個 Lock 類就是可重入的了。

鎖的公平性
 Starvation and Fairness 饑餓和公平

一個線程因為其他線程長期占有CPU而自己獲得不到,這種狀態(tài)稱為Starvation. 解決線程饑餓的方法是公平機制fairness公平機制,讓所有線程都能公平的有機會去獲得CPU。

導致饑餓的原因

高優(yōu)先級的線程占有了所有CPU處理時間,這樣低優(yōu)先級的線程獲得不到;

處于阻塞狀態(tài)的線程無限期被阻塞
Java 的同步代碼區(qū)也是一個導致饑餓的因素。Java 的同步代碼區(qū)對哪個線程允許進入的次序沒有任何保障。這就意味著理論上存在一個試圖進入該同步區(qū)的線程處于被永久堵塞的風險,因為其他線程總是能持續(xù)地先于它獲得訪問,這即是 “饑餓” 問題,而一個線程被 “饑餓致死” 正是因為它得不到 CPU 運行時間的機會

  

Java"s synchronized code blocks can be another cause of starvation.

處于等待狀態(tài)的對象無限期等待
如果多個線程處在 wait() 方法執(zhí)行上,而對其調(diào)用 notify() 不會保證哪一個線程會獲得喚醒,任何線程都有可能處于繼續(xù)等待的狀態(tài)。因此存在這樣一個風險:一個等待線程從來得不到喚醒,因為其他等待線程總是能被獲得喚醒。

這里細說一下:多線程通過共享一個object對象,來調(diào)用對象的wait/notifyAll 來導致線程等待或者喚醒; 每次一個線程進入同步塊,其他所有線程陷入等待狀態(tài);然后active線程調(diào)用notifyALL()函數(shù)喚醒所有等待線程,所有線程競爭,只有一個線程競爭成功,獲得CPU執(zhí)行。競爭失敗的線程處于就緒狀態(tài),長期競爭失敗的線程就會饑餓。

線程之間的對資源(object)競爭導致的饑餓,為了避免競爭,所以想辦法一次喚醒一個線程。也就是下面講的FairLock 公平鎖機制。

Implementing Fairness in Java

使用鎖lock來代替同步塊synchonized block

  

每一個調(diào)用 lock() 的線程都會進入一個隊列,當解鎖后,只有隊列里的第一個線程 (隊首)被允許鎖住 Fairlock 實例,所有其它的線程都將處于等待狀態(tài),直到他們處于隊列頭部。
公平鎖實現(xiàn)機制:為每一個線程創(chuàng)建一個專屬鎖對象(而非多個線程共享一個對象,來wait/notify()),然后用一個隊列來管理這些鎖對象,嘗試加鎖的線程會在各自的對象上等待,當一個線程unlock的時候,只通知隊列頭的鎖對象,以喚醒其對應(yīng)的線程

為了讓這個 Lock 類具有可重入性,我們需要對它做一點小的改動:

javapublic class FairLock {
    private boolean           isLocked       = false;
    private Thread            lockingThread  = null;
    private List waitingThreads =
            new ArrayList();

  public void lock() throws InterruptedException{
    QueueObject queueObject           = new QueueObject();
    boolean     isLockedForThisThread = true;
    synchronized(this){
        waitingThreads.add(queueObject);
    }

    while(isLockedForThisThread){
      synchronized(this){
        isLockedForThisThread =
            isLocked || waitingThreads.get(0) != queueObject;
        if(!isLockedForThisThread){
          isLocked = true;
           waitingThreads.remove(queueObject);
           lockingThread = Thread.currentThread();
           return;
         }
      }
      try{
        queueObject.doWait();
      }catch(InterruptedException e){
        synchronized(this) { waitingThreads.remove(queueObject); }
        throw e;
      }
    }
  }

  public synchronized void unlock(){
    if(this.lockingThread != Thread.currentThread()){
      throw new IllegalMonitorStateException(
        "Calling thread has not locked this lock");
    }
    isLocked      = false;
    lockingThread = null;
    if(waitingThreads.size() > 0){
      waitingThreads.get(0).doNotify();
    }
  }
}

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/64366.html

相關(guān)文章

  • 并發(fā)學習筆記 (6)

    摘要:每個通過網(wǎng)絡(luò)到達服務(wù)器的連接都被包裝成一個任務(wù)并且傳遞給線程池。線程池的線程會并發(fā)的處理連接上的請求。用線程池控制線程數(shù)量,其他線程排隊等候。實現(xiàn)包,線程池頂級接口是但是嚴格意義講并不是一個線程。此線程池支持定時以及周期性執(zhí)行任務(wù)的需求。 tutorial site1tutorial site2 一個問題: 每啟動一個新線程都會有相應(yīng)的性能開銷(涉及到OS的交互:創(chuàng)建線程,銷毀線程...

    superw 評論0 收藏0
  • Java 并發(fā)學習筆記(二)

    摘要:請參看前一篇文章并發(fā)學習筆記一原子性可見性有序性問題六等待通知機制什么是等待通知機制當線程不滿足某個條件,則進入等待狀態(tài)如果線程滿足要求的某個條件后,則通知等待的線程重新執(zhí)行。經(jīng)極客時間并發(fā)編程實戰(zhàn)專欄內(nèi)容學習整理 請參看前一篇文章:Java 并發(fā)學習筆記(一)——原子性、可見性、有序性問題 六、等待—通知機制 什么是等待通知—機制?當線程不滿足某個條件,則進入等待狀態(tài);如果線程滿足要...

    zgbgx 評論0 收藏0
  • Java 并發(fā)學習筆記(一)——原子性、可見性、有序性問題

    摘要:最后,總結(jié)一下,導致并發(fā)問題的三個源頭分別是原子性一個線程在執(zhí)行的過程當中不被中斷。可見性一個線程修改了共享變量,另一個線程能夠馬上看到,就叫做可見性。 計算機的 CPU、內(nèi)存、I/O 設(shè)備的速度一直存在較大的差異,依次是 CPU > 內(nèi)存 > I/O 設(shè)備,為了權(quán)衡這三者的速度差異,主要提出了三種解決辦法: CPU 增加了緩存,均衡和內(nèi)存的速度差異 發(fā)明了進程、線程,分時復用 CP...

    Chao 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<