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

          常用鏈接

          統(tǒng)計(jì)

          IT技術(shù)鏈接

          保險相關(guān)

          友情鏈接

          基金知識

          生活相關(guān)

          最新評論

          雙緩沖隊(duì)列

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

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

          接下來是玩具工廠,它得“不停地”生產(chǎn),所以,把它設(shè)計(jì)成一個線程類

          玩具工廠,將玩具對象,放到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();
                      }
                  }
                 
                   }
              }
          }
          注意到,在上面這段代碼當(dāng)中,必須得用synchronized (Tools.lT)將Tools.lT給加鎖。不然會出現(xiàn)線程同步問題(開銷就在這里)。

          再接下來,看看小孩是怎么“玩”玩具的:
          小孩類,從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);
                      }
                 
                  }
                  }
          }
          當(dāng)list不為空的時候,將list中的玩具對象,一個一個取出來,玩完!這個小孩可真夠厲害的,呵呵。可以想象為,該類的工作就是不停地向list中取出玩具對象。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類,他里面有個靜態(tài)的List對象:
          Tools類
          public class Tools {
              public static List<Toy>lT = new ArrayList<Toy>(10000);
          }

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

           

           


          好,31毫秒。


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

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

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

           

           

           


          哦哦,變成16毫秒了,著實(shí)提高了不少效果呢。看來,在處理同步的時候擠時間,是有發(fā)展空間的,呵呵。

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

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

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

          public class DoubleBufferList {

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

              /**
               * 構(gòu)造方法
               *
               * @param lP
               *            用來存放對象的隊(duì)列
               * @param lT
               *            用來取對象的隊(duì)列
               * @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();
              }

          }


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

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

           

           

           


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

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

           

           

           

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

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

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


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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 内黄县| 平顺县| 陈巴尔虎旗| 晋州市| 定州市| 乌鲁木齐县| 吉林省| 长治县| 依兰县| 清丰县| 佛学| 绥阳县| 凉山| 宣城市| 南安市| 江永县| 大冶市| 洛阳市| 崇州市| 丹棱县| 息烽县| 乌兰县| 西充县| 夏津县| 稷山县| 凤翔县| 遂宁市| 明水县| 定州市| 武宣县| 诸城市| 喀喇| 平远县| 舒兰市| 惠来县| 宁陕县| 柳林县| 洮南市| 将乐县| 怀化市| 永福县|