The important thing in life is to have a great aim , and the determination

          常用鏈接

          統計

          IT技術鏈接

          保險相關

          友情鏈接

          基金知識

          生活相關

          最新評論

          雙緩沖隊列

          提出問題:為啥要有雙緩沖隊列?
              引用09年9月《程序員》上的一句話:雙緩沖隊列就是沖著同步/互斥的開銷來的。我們知道,在多個線程并發訪問同一個資源的時候,需要特別注意線程的同步問題。稍稍不注意,哦活,程序結果不正確了。最經典的就是“銀行取錢”的例子,想想,都跟現金掛上鉤了,看來這真不容忽視。
              今天我們要談的不是如何去給資源加鎖解鎖來解決同步問題,今天的重點在于,如何將線程同步的開銷降低到我們力所能及的程度。如果你覺得,你可以通過增加硬件資源來彌補程序開銷,那么,你將不可能成為一個優秀的程序員。
              進入正題,先引入一個例子,兩個實體:一個是玩具工廠,它的工作就是不停地生產玩具;另外一個實體就是小孩,它的工作就是不停地從工廠拿玩具。小孩不可能直接到工廠去“拿”玩具吧?呵呵,媽媽是絕對不會放心的。所以,我們有一個“搬運工”,搬運工自然要具備“存放”的功能,不然他怎么將玩具帶給小孩呢,是吧。所以,我們先將搬運工定義為一個List,用來存放工廠生產出來的玩具。
          代碼如下
          玩具類,定義一個玩具實體
          public class Toy {
              private String name;

              public String getName() {
                  return name;
              }
          public void setName(String name) {
                  this.name = name;
              }
          }

          接下來是玩具工廠,它得“不停地”生產,所以,把它設計成一個線程類

          玩具工廠,將玩具對象,放到list中
          public class Factory extends Thread{
             

              public void run(){
                  while(true){
                 
                  Toy t = new Toy ();
                 
                  t.setName("玩具");
                  synchronized (Tools.lT){
                      Tools.lT.add(t);
          }
                      try {
                          Thread.sleep(2);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
                 
                   }
              }
          }
          注意到,在上面這段代碼當中,必須得用synchronized (Tools.lT)將Tools.lT給加鎖。不然會出現線程同步問題(開銷就在這里)。

          再接下來,看看小孩是怎么“玩”玩具的:
          小孩類,從list中取出玩具對象
          public class Kid extends Thread {
              long time1 = System.currentTimeMillis();
              int count = 0;
              public void run() {
                  while(true){
                 
                      synchronized(Tools.lT){
                          if(Tools.lT.size()!=0)
                      Tools.lT.remove(0);
                      }
                      count++;
                      if(count==100000){
                          javax.swing.JOptionPane.showMessageDialog(null, "用時間: "+(System.currentTimeMillis()-time1));
                          System.exit(0);
                      }
                 
                  }
                  }
          }
          當list不為空的時候,將list中的玩具對象,一個一個取出來,玩完!這個小孩可真夠厲害的,呵呵??梢韵胂鬄椋擃惖墓ぷ骶褪遣煌5叵騦ist中取出玩具對象。OK,再來編寫方法,如下

          主方法
          public class MainTest {

              /**
               * @param args
               */
              public static void main(String[] args) {
                  Factory f = new Factory();
                  f.start();
                  Kid k = new Kid();
                  k.start();
                 
              }
          }

          最后是Tools類,他里面有個靜態的List對象:
          Tools類
          public class Tools {
              public static List<Toy>lT = new ArrayList<Toy>(10000);
          }

          這樣,我們運行一下主方法,看看我們這位“天才”玩完100000個玩具,得花銷多少的時間。

           

           


          好,31毫秒。


              這是我們的第一種解決方法,下面再來看第二種解決方法:
          其實我們在Factory類和Kid類中都進行了同步處理,這樣一來,浪費了很多時間,到底是不是這樣的呢?我們可不可以直接用一個不用處理線程同步的容器來放Toy類對象呢?這樣以來是不是就可以節省很多開銷了?這個想法是有道理的,但是,事實是不是這樣的呢?馬上實踐!

          代碼就不具體貼出來了,只是我們在Tools類中用到的是一個如下的對象
          Public static LinkedBlockingQueue<Toy> lT= new LinkedBlockingQueue<Toy>(1000);

          對,阻塞隊列,這樣我們就只管往里面取,從里面拿了,不用自己考慮同步問題,Factory類和Kid類中也不同特意去加關鍵字進行同步了。
          那么這種方案的結果是多少呢?同樣是100000個玩具,看結果
            

           

           

           


          哦哦,變成16毫秒了,著實提高了不少效果呢??磥恚谔幚硗降臅r候擠時間,是有發展空間的,呵呵。

          等等,有人要發話了,你在這磨嘰了半天,還是沒有說什么是雙緩沖啊,對!有了前面的兩種方案,我們再來看看“雙緩沖隊列”。

          所謂雙緩沖隊列,自然起碼要有兩個隊列吧,呵呵,在這個例子中,我們可以設計兩個List來存放工廠生產出來的玩具對象。
          下面分析一下:
          兩個List,一個用來存,一個用來取。有點迷糊?就是有一個listP從工廠那里得到玩具對象,另外一個listT就專門把它得到的玩具對象送去給 Kid類處理。當listT變成空的了以后,再將listP中在這段時間內取到的所有玩具對象放到listT中,好,這完了之后,他們兩個就又各自干各自的去了:listP再去取,listT再去送。這樣是不是就減少了很多次的線程同步呢?至少,在它們交換之前,listP是完全被工廠類線程占有,listT是完全被Kid類線程占有的,不用處理同步。只有在listT放完了,沒得給了,再去跟ListP換過來,這個時候就要處理同步了。

          跟實際聯系一下,有兩個搬運工A,B,A在工廠,專門從工廠取玩具;B在小孩子身邊,專門送玩具給小孩玩。當B身上沒有玩具了,自然要回A那里,把A身上的玩具全部拿過來,再來送給小孩玩。在A還有玩具的時候,A和B是在各自的線程里被處理的,即A在拿,B在給。不用擔心同步問題。
          這樣以來,處理同步問題的次數是不是大大減少了呢?沒錯,就是這樣的。那么怎么跟代碼結合呢?
          我們要設計一個監視線程,監視listP是不是空了,要是空了,把它同步起來,把listT也同步起來,讓他們交換。完了就各自干各自的了。
          我們來看看這個監視類:

          public class DoubleBufferList {

              private List<Object> lP;
              private List<Object> lT;
              private int gap;

              /**
               * 構造方法
               *
               * @param lP
               *            用來存放對象的隊列
               * @param lT
               *            用來取對象的隊列
               * @param gap
               *            交換的間隔
               */
              public DoubleBufferList(List lP, List lT, int gap) {
                  this.lP = lP;
                  this.lT = lT;
                  this.gap = gap;
              }

              public void check() {
                  Runnable runner = new Runnable() {
                      public void run() {
                          while (true) {
                              if (lT.size() == 0) {
                                  synchronized (lT) {
                                      synchronized (lP) {
                                          lT.addAll(lP);
                                      }
                                      lP.clear();
                                  }
                              }
                              try {
                                  Thread.sleep(gap);

                              } catch (InterruptedException e) {
                                  e.printStackTrace();
                              }
                          }
                      }
                  };
                  Thread thread = new Thread(runner);
                  thread.start();
              }

          }


          這個類中的線程方法就是用來交換的,目的就是為了減少處理同步的次數。這種方案中,Facotry類和Kid類跟前面單個List的情況差不多,就不再給出了。只是有一點要注意,Facory類中將玩具對象是放到了lP中,而Kid類中,也只是從lT中去取對象??纯碩ools類中,多了一個變量:
          Tools類,聲明兩個隊列
              public static List<Toy>lT = new ArrayList<Toy>(10000);
              public static List<Toy>lP = new ArrayList<Toy>(10000);

          同樣是讓我們的“天才”玩完100000個玩具,來看看運行需要的時間:

           

           

           


           
          哈哈,似乎跟我們上面的第二種方案,單阻塞隊列,沒有太大的差異。怎么解釋呢?
          不用著急,來,我將額定的玩具量后多加個“0”,讓他玩完1000000個!改一下單阻塞隊列方案的輸出結果,給他們一個標記。再來看看結果:

           
          效果出來了吧,我們再加大量,讓他們同時處理10000000個玩具對象: 

           

           

           

          充分說明,使用雙緩沖隊列,比單緩沖阻塞隊列的效果要好,更別說單緩沖隊列了。

          總結:
          從上面的分析,我們可以得知,在處理線程同步的時候,是要花費我們的時間的,雖然在有些時候,這樣的花費是我們可以接受的,但是在很多情況下,如果我們能注意到這樣的浪費,并且及時地完善我們的程序,這樣可以更大限度地提高我們程序的運行效率。尤其是在大的程序里面,這樣的效果體現得更明顯。而往往越大的系統,對性能的要求也就越高。

          posted on 2011-03-17 16:08 鴻雁 閱讀(1254) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 安溪县| 剑阁县| 太原市| 平潭县| 长白| 内丘县| 济源市| 望江县| 柘城县| 平利县| 楚雄市| 江油市| 中江县| 双峰县| 麻栗坡县| 贵定县| 崇州市| 长岛县| 丰台区| 平罗县| 乐山市| 梨树县| 平顺县| 丘北县| 宁津县| 紫云| 桑日县| 牡丹江市| 大关县| 邯郸县| 崇义县| 普兰店市| 龙游县| 广汉市| 汽车| 鄂伦春自治旗| 郯城县| 泰安市| 双柏县| 湛江市| 灌阳县|