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

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

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

    java something

    不要以為......很遙遠
    隨筆 - 23, 文章 - 1, 評論 - 2, 引用 - 0
    數據加載中……

    線程的同步與共享

    線程的同步與共享

    前面程序中的線程都是獨立的、異步執行的線程。但在很多情況下,多個線程需要共享數據資源,這就涉及到線程的同步與資源共享的問題。

    資源沖突

    下面的例子說明,多個線程共享資源,如果不加以控制可能會產生沖突。

    程序CounterTest.java

     

    class Num
    {
     private int x = 0;
     private int y = 0;

     void increase()
     {
      x++;
      y++;
     }

     void testEqual()
     {
      System.out.println(x + "," + y + ":" + (x == y));
     }
    }

    class Counter extends Thread
    {
     private Num num;

     Counter(Num num)
     {
      this.num = num;
     }

     public void run()
     {
      while (true)
      {
       num.increase();
      }
     }
    }

    public class CounterTest
    {
     public static void main(String[] args)
     {
      Num num = new Num();
      Thread count1 = new Counter(num);
      Thread count2 = new Counter(num);
      count1.start();
      count2.start();

      for (int i = 0; i < 100; i++)
      {
       num.testEqual();
       try
       {
        Thread.sleep(100);
       } catch (InterruptedException e)
       {
       }
      }
     }
    }

     

     

    上述程序在CounterTest類的main()方法中創建了兩個線程類Counter的對象count1count2,這兩個對象共享一個Num類的對象num。兩個線程對象開始運行后,都調用同一個對象numincrease()方法來增加num對象的xy的值。在main()方法的for()循環中輸出num對象的xy的值。程序輸出結果有些xy的值相等,大部分xy的值不相等。

    出現上述情況的原因是:兩個線程對象同時操作一個num對象的同一段代碼,通常將這段代碼段稱為臨界區(critical sections)。在線程執行時,可能一個線程執行了x++語句而尚未執行y++語句時,系統調度另一個線程對象執行x++y++,這時在主線程中調用testEqual()方法輸出xy的值不相等

    對象鎖的實現

    上述程序的運行結果說明了多個線程訪問同一個對象出現了沖突,為了保證運行結果正確(xy的值總相等),可以使用Java語言的synchronized關鍵字,用該關鍵字修飾方法。用synchronized關鍵字修飾的方法稱為同步方法,Java平臺為每個具有synchronized代碼段的對象關聯一個對象鎖(object lock)。這樣任何線程在訪問對象的同步方法時,首先必須獲得對象鎖,然后才能進入synchronized方法,這時其他線程就不能再同時訪問該對象的同步方法了(包括其他的同步方法)

    通常有兩種方法實現對象鎖:

    (1) 在方法的聲明中使用synchronized關鍵字,表明該方法為同步方法。

    對于上面的程序我們可以在定義Num類的increase()testEqual()方法時,在它們前面加上synchronized關鍵字,如下所示:

    synchronized void increase(){

        x++;

        y++;

    }

    synchronized void testEqual(){

        System.out.println(x+","+y+":"+(x==y)+":"+(x<y));

    }

    一個方法使用synchronized關鍵字修飾后,當一個線程調用該方法時,必須先獲得對象鎖,只有在獲得對象鎖以后才能進入synchronized方法。一個時刻對象鎖只能被一個線程持有。如果對象鎖正在被一個線程持有,其他線程就不能獲得該對象鎖,其他線程就必須等待持有該對象鎖的線程釋放鎖。

    如果類的方法使用了synchronized關鍵字修飾,則稱該類對象是線程安全的,否則是線程不安全的。

    如果只為increase()方法添加synchronized 關鍵字,結果還會出現xy的值不相等的情況.
        
        (2)
    前面實現對象鎖是在方法前加上synchronized 關鍵字,這對于我們自己定義的類很容易實現,但如果使用類庫中的類或別人定義的類在調用一個沒有使用synchronized關鍵字修飾的方法時,又要獲得對象鎖,可以使用下面的格式:

    synchronized(object){

       //方法調用

    }

    假如Num類的increase()方法沒有使用synchronized 關鍵字,我們在定義Counter類的run()方法時可以按如下方法使用synchronized為部分代碼加鎖。

    public void run(){

        while(true){

    synchronized (num){

           num.increase();

         }

        }

    }

    同時在main()方法中調用testEqual()方法也用synchronized關鍵字修飾,這樣得到的結果相同。

    synchronized(num){

        num.testEqual();

    }

    對象鎖的獲得和釋放是由Java運行時系統自動完成的。

    每個類也可以有類鎖。類鎖控制對類的synchronized static代碼的訪問。請看下面的例子:

    public class X{

     static int x, y;

     static synchronized void foo(){

         x++;

    y++;

    }

    }

    foo()方法被調用時,調用線程必須獲得X類的類鎖。

    3  線程間的同步控制

    在多線程的程序中,除了要防止資源沖突外,有時還要保證線程的同步。下面通過生產者-消費者模型來說明線程的同步與資源共享的問題。

    假設有一個生產者(Producer),一個消費者(Consumer)。生產者產生0~9的整數,將它們存儲在倉庫(CubbyHole)的對象中并打印出這些數來;消費者從倉庫中取出這些整數并將其也打印出來。同時要求生產者產生一個數字,消費者取得一個數字,這就涉及到兩個線程的同步問題。

    這個問題就可以通過兩個線程實現生產者和消費者,它們共享CubbyHole一個對象。如果不加控制就得不到預期的結果。

    1. 不同步的設計

    首先我們設計用于存儲數據的類,該類的定義如下:

    程序 CubbyHole.java

    class CubbyHole{

      private int content ;

    public synchronized void put(int value){

    content = value;

    }

      public synchronized int get(){

    return content ;

    }

    }

    _____________________________________________________________________________

    CubbyHole類使用一個私有成員變量content用來存放整數,put()方法和get()方法用來設置變量content的值。CubbyHole對象為共享資源,所以用synchronized關鍵字修飾。當put()方法或get()方法被調用時,線程即獲得了對象鎖,從而可以避免資源沖突。

    這樣當Producer對象調用put()方法是,它鎖定了該對象,Consumer對象就不能調用get()方法。當put()方法返回時,Producer對象釋放了CubbyHole的鎖。類似地,當Consumer對象調用CubbyHoleget()方法時,它也鎖定該對象,防止Producer對象調用put()方法。

    接下來我們看ProducerConsumer的定義,這兩個類的定義如下:

    程序 Producer.java

    public class Producer extends Thread {

        private CubbyHole cubbyhole;

        private int number;

        public Producer(CubbyHole c, int number) {

            cubbyhole = c;

            this.number = number;

        }

        public void run() {

           for (int i = 0; i < 10; i++) {

              cubbyhole.put(i);

              System.out.println("Producer #" + this.number + " put: " + i);

              try {

                    sleep((int)(Math.random() * 100));

               } catch (InterruptedException e) { }

            }

        }

    }

    _____________________________________________________________________________

    Producer類中定義了一個CubbyHole類型的成員變量cubbyhole,它用來存儲產生的整數,另一個成員變量number用來記錄線程號。這兩個變量通過構造方法傳遞得到。在該類的run()方法中,通過一個循環產生10個整數,每次產生一個整數,調用cubbyhole對象的put()方法將其存入該對象中,同時輸出該數。

    下面是Consumer類的定義:

    程序 Consumer.java

    public class Consumer extends Thread {

        private CubbyHole cubbyhole;

        private int number;

        public Consumer(CubbyHole c, int number) {

            cubbyhole = c;

            this.number = number;

        }

        public void run() {

            int value = 0;

            for (int i = 0; i < 10; i++) {

                value = cubbyhole.get();

          System.out.println("Consumer #" + this.number + " got: " + value);

            }

        }

    }

    _____________________________________________________________________________

    Consumer類的run()方法中也是一個循環,每次調用cubbyholeget()方法返回當前存儲的整數,然后輸出。

    下面是主程序,在該程序的main()方法中創建一個CubbyHole對象c,一個Producer對象p1,一個Consumer對象c1,然后啟動兩個線程。

    程序 ProducerConsumerTest.java

    public class ProducerConsumerTest {

        public static void main(String[] args) {

            CubbyHole c = new CubbyHole();

            Producer p1 = new Producer(c, 1);

            Consumer c1 = new Consumer(c, 1);

            p1.start();

            c1.start();

        }

    }

    _____________________________________________________________________________

    該程序中對CubbyHole類的設計,盡管使用了synchronized關鍵字實現了對象鎖,但這還不夠。程序運行可能出現下面兩種情況:

    如果生產者的速度比消費者快,那么在消費者來不及取前一個數據之前,生產者又產生了新的數據,于是消費者很可能會跳過前一個數據,這樣就會產生下面的結果:

    Consumer: 3

    Producer: 4

    Producer: 5

    Consumer: 5

    反之,如果消費者比生產者快,消費者可能兩次取同一個數據,可能產生下面的結果:

    Producer: 4

    Consumer: 4

    Consumer: 4

    Producer: 5

    2. 監視器模型

    為了避免上述情況發生,就必須使生產者線程向CubbyHole對象中存儲數據與消費者線程從CubbyHole對象中取得數據同步起來。為了達到這一目的,在程序中可以采用監視器(monitor)模型,同時通過調用對象的wait()方法和notify()方法實現同步。

    下面是修改后的CubbyHole類的定義:

    程序CubbyHole.java

    class CubbyHole{

      private int content ;

      private boolean available=false;

     

    public synchronized void put(int value){

     while(available==true){

          try{

    wait();

    }catch(InterruptedException e){}

    }

    content =value;

    available=true;

    notifyAll();

    }

      public synchronized int get(){

        while(available==false){

          try{

    wait();

    }catch(InterruptedException e){}

        }

    available=false;

    notifyAll();

    return content;

    }

    }

    _____________________________________________________________________________

    這里有一個boolean型的私有成員變量available用來指示內容是否可取。當availabletrue時表示數據已經產生還沒被取走,當availablefalse時表示數據已被取走還沒有存放新的數據。

    當生產者線程進入put()方法時,首先檢查available的值,若其為false,才可執行put()方法,若其為true,說明數據還沒有被取走,該線程必須等待。因此在put()方法中調用CubbyHole對象的wait()方法等待。調用對象的wait()方法使線程進入等待狀態,同時釋放對象鎖。直到另一個線程對象調用了notify()notifyAll()方法,該線程才可恢復運行。

    類似地,當消費者線程進入get()方法時,也是先檢查available的值,若其為true,才可執行get()方法,若其為false,說明還沒有數據,該線程必須等待。因此在get()方法中調用CubbyHole對象的wait()方法等待。調用對象的wait()方法使線程進入等待狀態,同時釋放對象鎖。

    上述過程就是監視器模型,其中CubbyHole對象為監視器。通過監視器模型可以保證生產者線程和消費者線程同步,結果正確。

    程序的運行結果如下:

    特別注意:wait()notify()notifyAll()方法是Object類定義的方法,并且這些方法只能用在synchronized代碼段中。它們的定義格式如下:

    ·         public final void wait()

    ·         public final void wait(long timeout)

    ·         public final void wait(long timeout, int nanos)

    當前線程必須具有對象監視器的鎖,當調用該方法時線程釋放監視器的鎖。調用這些方法使當前線程進入等待(阻塞)狀態,直到另一個線程調用了該對象的notify()方法或notifyAll()方法,該線程重新進入運行狀態,恢復執行。

    timeoutnanos為等待的時間的毫秒和納秒,當時間到或其他對象調用了該對象的notify()方法或notifyAll()方法,該線程重新進入運行狀態,恢復執行。

    wait()的聲明拋出了InterruptedException,因此程序中必須捕獲或聲明拋出該異常。

    ·         public final void notify()

    ·         public final void notifyAll()

    喚醒處于等待該對象鎖的一個或所有的線程繼續執行,通常使用notifyAll()方法。

        在生產者/消費者的例子中,CubbyHole類的putget方法就是臨界區。當生產者修改它時,消費者不能問CubbyHole對象;當消費者取得值時,生產者也不能修改它。








     

    posted on 2011-09-02 01:38 Jamie 閱讀(477) 評論(0)  編輯  收藏 所屬分類: 多線程

    主站蜘蛛池模板: 久久精品国产亚洲av高清漫画 | 污视频在线免费观看| 亚洲熟女综合一区二区三区| 亚洲av伊人久久综合密臀性色 | 疯狂做受xxxx高潮视频免费| 亚洲人成网网址在线看| 亚洲AV午夜成人影院老师机影院| 一本色道久久88亚洲综合| 毛片免费在线视频| 7m凹凸精品分类大全免费| 叮咚影视在线观看免费完整版| 日本系列1页亚洲系列| va天堂va亚洲va影视中文字幕| 亚洲精品高清久久| 国产亚洲福利精品一区| 亚洲欧洲自拍拍偷精品 美利坚| 日本免费福利视频| 免费人成在线视频| 可以免费看黄的网站| 91精品免费高清在线| 久9久9精品免费观看| a毛片在线还看免费网站| 精品国产免费一区二区三区| 国产精品亚洲一区二区三区在线观看| 狠狠色香婷婷久久亚洲精品| 亚洲国产精品久久久久秋霞影院 | 1000部国产成人免费视频| 国产精品免费无遮挡无码永久视频| 久久最新免费视频| 中文字幕无码毛片免费看| 国产免费伦精品一区二区三区| 日韩在线视频免费| 一区二区三区免费高清视频| 成人免费网站视频www| 羞羞视频在线观看免费| 欧亚一级毛片免费看| www成人免费视频| 99精品视频免费| 午夜网站在线观看免费完整高清观看| 怡红院免费的全部视频| 亚洲欧洲免费视频|