• <input id="qucwm"><u id="qucwm"></u></input>
  • <menu id="qucwm"></menu>
  • <input id="qucwm"><tt id="qucwm"></tt></input>
  • <input id="qucwm"><acronym id="qucwm"></acronym></input>
  • 基本線程同步(五)使用Lock同步代碼塊

    聲明:本文是《 Java 7 Concurrency Cookbook 》的第二章,作者: Javier Fernández González ? ? 譯者:許巧輝 校對:方騰飛

    使用Lock同步代碼塊

    Java提供另外的機制用來同步代碼塊。它比synchronized關鍵字更加強大、靈活。它是基于Lock接口和實現它的類(如ReentrantLock)。這種機制有如下優勢:

    • 它允許以一種更靈活的方式來構建synchronized塊。使用synchronized關鍵字,你必須以結構化方式得到釋放synchronized代碼塊的控制權。Lock接口允許你獲得更復雜的結構來實現你的臨界區。
    • Lock 接口比synchronized關鍵字提供更多額外的功能。新功能之一是實現的tryLock()方法。這種方法試圖獲取鎖的控制權并且如果它不能獲取該鎖,是因為其他線程在使用這個鎖,它將返回這個鎖。使用synchronized關鍵字,當線程A試圖執行synchronized代碼塊,如果線程B正在執行它,那么線程A將阻塞直到線程B執行完synchronized代碼塊。使用鎖,你可以執行tryLock()方法,這個方法返回一個 Boolean值表示,是否有其他線程正在運行這個鎖所保護的代碼。
    • 當有多個讀者和一個寫者時,Lock接口允許讀寫操作分離。
    • Lock接口比synchronized關鍵字提供更好的性能。

    在這個指南中,你將學習如何通過鎖來同步代碼塊和通過Lock接口及其實現者ReentrantLock類來創建臨界區,實現一個程序來模擬打印隊列。

    準備工作

    這個指南的例子使用Eclipse IDE實現。如果你使用Eclipse或其他IDE,如NetBeans,打開它并創建一個新的Java項目。

    如何做…

    按以下步驟來實現的這個例子:

    1.創建PrintQueue類,來實現打印隊列。

    public class PrintQueue {
    

    2.聲明一個Lock對象,并且使用ReentrantLock類的一個新對象來初始化它。

    private final Lock queueLock=new ReentrantLock();
    

    3.實現printJob()方法,它將接收Object對象作為參數,并且不會返回任何值。

    public void printJob(Object document){
    

    4.在printJob()方法內部,通過調用lock()方法來獲取Lock對象的控制權。

    queueLock.lock();
    

    5.然后,包含以下代碼來模擬文檔的打?。?/p>

    try {
    Long duration=(long)(Math.random()*10000);
    System.out.println(Thread.currentThread().getName()+ ":
    PrintQueue: Printing a Job during "+(duration/1000)+
    " seconds");
    Thread.sleep(duration);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    

    6.最后,通過調用unlock()方法來釋放Lock對象的控制。

    finally {
    queueLock.unlock();
    }
    

    7.創建一個Job類,并指定它實現Runnable接口。

    public class Job implements Runnable {
    

    8.聲明一個PrintQueue類的對象,并通過實現類(Job類)的構造器來初始化這個對象。

    private PrintQueue printQueue;
    public Job(PrintQueue printQueue){
    this.printQueue=printQueue;
    }
    

    9.實現run()方法,它使用PrintQueue對象來發送一個打印任務。

    @Override
    public void run() {
    System.out.printf("%s: Going to print a document\n", Thread.
    currentThread().getName());
    printQueue.printJob(new Object());
    System.out.printf("%s: The document has been printed\n",
    Thread.currentThread().getName());
    }
    

    10.通過創建類名為Main,且包括main()方法來實現這個示例的主類。

    
    public class Main {
    public static void main(String[] args) {
    
    

    11.創建一個共享的PrintQueue對象。

    
    PrintQueue printQueue=new PrintQueue();
    
    

    12.創建10個Job對象,并且使用10個線程來運行它們。

    
    Thread thread[]=new Thread[10];
    for (int i=0; i<10; i++){
    thread[i]=new Thread(new Job(printQueue),"Thread "+ i);
    }
    
    

    13.啟動這10個線程。

    
    for (int i=0; i<10; i++){
    thread[i].start();
    }
    
    

    它是如何工作的…

    從以下截圖,你可以看到執行這個示例一部分的輸出:

    4

    在 printJob()中,PrintQueue類是這個示例的關鍵所在。當我們通過鎖來實現一個臨界區并且保證只有一個執行線程能運行一個代碼塊,我們必 須創建一個ReentrantLock對象。在臨界區的起始部分,我們必須通過使用lock()方法來獲得鎖的控制權。當一個線程A調用這個方法時,如果 沒有其他線程持有這個鎖的控制權,那么這個方法就會給線程A分配這個鎖的控制權并且立即返回允許線程A執行這個臨界區。否則,如果其他線程B正在執行由這 個鎖控制的臨界區,lock()方法將會使線程A睡眠直到線程B完成這個臨界區的執行。

    在臨界區的尾部,我們必須使用unlock()方法來釋放鎖的控制權,允許其他線程運行這個臨界區。如果你在臨界區的尾部沒有調用unlock()方法,那么其他正在等待該代碼塊的線程將會永遠等待,造成 死鎖情況。如果你在臨界區使用try-catch代碼塊,別忘了在finally部分的內部包含unlock()方法的代碼。

    不止這些…

    Lock 接口(和ReentrantLock類)包含其他方法來獲取鎖的控制權,那就是tryLock()方法。這個方法與lock()方法的最大區別是,如果一 個線程調用這個方法不能獲取Lock接口的控制權時,將會立即返回并且不會使這個線程進入睡眠。這個方法返回一個boolean值,true表示這個線程 獲取了鎖的控制權,false則表示沒有。

    注釋:考慮到這個方法的結果,并采取相應的措施,這是程序員的責任。如果這個方法返回false值,預計你的程序不會執行這個臨界區。如果是這樣,你可能會在你的應用程序中得到錯誤的結果。

    ReentrantLock類也允許遞歸調用(鎖的可重入性,譯者注),當一個線程有鎖的控制權并且使用遞歸調用,它延續了鎖的控制權,所以調用lock()方法將會立即返回并且繼續遞歸調用的執行。此外,我們也可以調用其他方法。

    更多信息

    你必須要非常小心使用鎖來避免死鎖,這種情況發生在,當兩個或兩個以上的線程被阻塞等待將永遠不會解開的鎖。比如,線程A鎖定Lock(X)而線程B鎖定 Lock(Y)。如果現在,線程A試圖鎖住Lock(Y)而線程B同時也試圖鎖住Lock(X),這兩個線程將無限期地被阻塞,因為它們等待的鎖將不會被解開。請注意,這個問題的發生是因為這兩個線程嘗試以相反的順序獲取鎖(譯者注:鎖順序死鎖)。在附錄中,提供了一些很好的并發編程設計的建議,適當的設計并發應用程序,來避免這些死鎖問題。

    參見

    • 在第2章,基本線程同步中的同步方法指南
    • 在第2章,基本線程同步中的在鎖中使用多條件的指南
    • 在第8章,測試并發應用程序中的監控Lock接口的指南

    原創文章,轉載請注明: 轉載自并發編程網 – www.okfdzs1913.com本文鏈接地址: 基本線程同步(五)使用Lock同步代碼塊


    FavoriteLoading添加本文到我的收藏
    • Trackback 關閉
    • 評論 (4)
      • Ryan
      • 2013/10/16 11:37下午

      這段代碼使用lock不曉得有什么意義?貌似只是一端只讀代碼,僅僅是為了演示lock的用途,感覺這個例子不太恰當。

      不曉得是不是我理解有問題,方便的話麻煩作者指導一下,不勝感激

        • moodmass
        • 2014/04/30 4:14下午

        保證這段代碼
        Long duration=(long)(Math.random()*10000);
        System.out.println(Thread.currentThread().getName()+ “:
        PrintQueue: Printing a Job during “+(duration/1000)+
        ” seconds”);
        Thread.sleep(duration);
        執行的時候不會被中斷~

      • huoer
      • 2014/09/02 3:31下午

      我覺得上面第9步的打印代碼放在printJob(Object document)方法中比較好,否則出現上圖的結果代碼的幾率很小】、

      public void printJob(Object document){
      queueLock.lock();
      System.out.printf(“%s: Going to print a document\n”,
      Thread.currentThread().getName());
      try {
      Long duration = (long)(Math.random() * 10000);
      System.out.println(Thread.currentThread().getName()+ “:” +
      “PrintQueue: Printing a Job during “+(duration/1000)+
      ” seconds”);
      Thread.sleep(duration);
      } catch (InterruptedException e) {
      e.printStackTrace();
      }finally{
      System.out.printf(“%s: The document has been printed\n”,
      Thread.currentThread().getName());
      queueLock.unlock();
      }
      }

      結果:
      Thread0: Going to print a document
      Thread0:PrintQueue: Printing a Job during 5 seconds
      Thread0: The document has been printed
      Thread1: Going to print a document
      Thread1:PrintQueue: Printing a Job during 7 seconds
      Thread1: The document has been printed
      Thread3: Going to print a document
      Thread3:PrintQueue: Printing a Job during 1 seconds
      Thread3: The document has been printed
      Thread5: Going to print a document
      Thread5:PrintQueue: Printing a Job during 2 seconds
      Thread5: The document has been printed
      Thread7: Going to print a document
      Thread7:PrintQueue: Printing a Job during 3 seconds
      Thread7: The document has been printed
      Thread9: Going to print a document
      Thread9:PrintQueue: Printing a Job during 3 seconds
      Thread9: The document has been printed
      Thread2: Going to print a document
      Thread2:PrintQueue: Printing a Job during 6 seconds
      Thread2: The document has been printed
      Thread4: Going to print a document
      Thread4:PrintQueue: Printing a Job during 6 seconds
      Thread4: The document has been printed
      Thread6: Going to print a document
      Thread6:PrintQueue: Printing a Job during 4 seconds
      Thread6: The document has been printed
      Thread8: Going to print a document
      Thread8:PrintQueue: Printing a Job during 3 seconds
      Thread8: The document has been printed

    您必須 登陸 后才能發表評論

    return top

    淘宝彩票网 wo4| ek4| wgq| m4c| qsw| 2ui| ww2| mis| i2m| wic| 3ws| gq3| gmq| u3c| s3c| okc| 3ew| es1| kkg| q2y| guk| 2aq| qc2| uqo| k2w| oqu| 2sk| gok| ce1| 1om| yk1| ywk| k1a| oca| 1iq| ue1| kke| u2c| ygc| 2ks| gue| eg0| gio| e0w| ukq| 0ke| wm0| gqa| m1y| oew| 1si| sq1| qeo| e9w| y9e| soi| 9uy| egy| 0ys| ee0| wyq| u0e| yyc| 0ou| wi8| kwm| y9y| q9w| mog| 9eu| gs9| amg| g9q| qqk| 9su| ec8| kyq| q8e| ugm| 8gi| 8ei| ma8| ceo| q8a| iia| 9ek| iim| 9ui| oo7| uyq|