天天看海

          每天都想看海

          一個(gè)能夠證明i++在多線程下非原子化的例子及其改進(jìn)

          主程序運(yùn)行1000個(gè)子線程(太少了結(jié)果不容易出來),每個(gè)子線程執(zhí)行指向同一整數(shù)引用的i++,當(dāng)1000個(gè)子線程運(yùn)行完畢后,主線程繼續(xù)運(yùn)行,輸出最終的i值。很顯然,如果最終輸出的結(jié)果i值不等于1000,那么中間i++操作一定被分解了。程序代碼如下:

          【版本一】

          import cn.yuzhe.multi.util.AllFinished;

          public class ThreadSafeClass {

          public static void main(String[] args) {
                   
          //要?jiǎng)?chuàng)建的子線程數(shù)
                       int threadNum=1000;                          
            
                   
          //每個(gè)子線程都要引用的對(duì)象
                       A_Integer bb=new A_Integer();
            
                  
          //創(chuàng)建能判斷所有子線程是否結(jié)束的對(duì)象,用于保證主線程能在多有子線程都結(jié)束后再執(zhí)行余下的代碼,
                       
          //防止主線程提前于子線程結(jié)束
                       AllFinished af=new AllFinished(threadNum);  
            
                   
          //創(chuàng)建多個(gè)子線程對(duì)象
                       NoAtomicIntegerThread[] at=new NoAtomicIntegerThread[threadNum];
            
                       
          for(int i=0;i<threadNum;i++){
                             at[i]
          =new NoAtomicIntegerThread(bb,i,af);
                       }

            
                       
          for(int i=0;i<threadNum;i++){
                             at[i].start();
                       }
           
            
                       
          while(true){
                             
          if( af.isAllFinished() ){
                                   System.out.println(
          "主線程運(yùn)行結(jié)束,應(yīng)該獲得的最終值為"+threadNum
                                                    
          +";實(shí)際最終值為"+bb.int_I);
                                   
          break;
                             }

             
                            
          int sleep=500;    //睡眠一段時(shí)間,釋放時(shí)間片
                            try {
                                  Thread.sleep(sleep);
                            }
           catch (InterruptedException e) {
                                  e.printStackTrace();
                            }

                       }
           
                   }

          }


          class NoAtomicIntegerThread extends Thread{
                    
          public A_Integer jj;
                    
          private int id;
                    
          private AllFinished af;

          public void run() {
                       jj.int_I
          ++;
                       af.setItemFinished(id);
          //           System.out.println(jj.int_I);              //輸出中間結(jié)果
          }


          public NoAtomicIntegerThread(A_Integer __j,int _id,AllFinished _af) {
                       
          super();
                       
          this.jj=__j;
                       
          this.id=_id;
                       
          this.af=_af;
          }

          }


          class A_Integer
          {
                      
          public int int_I=0;
          }



          這里要用到一個(gè)我自己定義的util類:

          /**
          @author 紅樓無夢,JAVA多線程QQ群:34237757
          * Date:2008-8-13 09:28
          * Function: 主線程創(chuàng)建多個(gè)分線程,保證主線程在所有分線程都結(jié)束后才能運(yùn)行余下的代碼,直至結(jié)束。
          */

          public class AllFinished {
                      
          private boolean[] isItemsFinished;
                      
          private int num;

                      
          public AllFinished(int itemNum) {
                            
          super();
            
                            num
          =itemNum;
                            isItemsFinished
          =new boolean[num];
                            
          for(int i=0;i<num;i++)
                                  isItemsFinished[i]
          =false;
                      }


                      
          public synchronized boolean setItemFinished(int i)
                      
          {
                            
          if(i>=num)
                                  
          return false;
                            
          this.isItemsFinished[i]=true;
                            
          return true;
                      }


                      
          public synchronized boolean isAllFinished()
                      
          {
                            
          for(int i=0;i<num;i++){
                                  
          if(!this.isItemsFinished[i])
                                        
          return false;
                            }

                            
          return true;
                      }

          }


          例子需要運(yùn)行多次才能看到結(jié)果,當(dāng)實(shí)際結(jié)果和理想結(jié)果不一樣的時(shí)候,很顯然,i++被分解了,結(jié)果的部分輸入如下:
          ......
          996
          997
          998
          主線程運(yùn)行結(jié)束,應(yīng)該獲得的最終值為1000;實(shí)際最終值為998。

          =====================================================
          【改進(jìn)一】下面的代碼用AtomicInteger進(jìn)行了改進(jìn),使得i++操作被原子化,代碼如下:

          public class ThreadSafeClass {

                    
          public static void main(String[] args) {
                
          //要?jiǎng)?chuàng)建的子線程數(shù)
                    int threadNum=1000;                          
            
                
          //每個(gè)子線程都要引用的對(duì)象
                    AtomicInteger bb=new AtomicInteger(0);
            
                
          //創(chuàng)建能判斷所有子線程是否結(jié)束的對(duì)象,用于保證主線程能在多有子線程都結(jié)束后再執(zhí)行余下的代碼,
                    
          //防止主線程提前于子線程結(jié)束
                    AllFinished af=new AllFinished(threadNum);  
            
                
          //多個(gè)子線程對(duì)象
                    AtomicIntegerThread[] at=new AtomicIntegerThread[threadNum];
            
                    
          for(int i=0;i<threadNum;i++){
                         at[i]
          =new AtomicIntegerThread(bb,i,af);
                    }

            
                    
          for(int i=0;i<threadNum;i++){
                         at[i].start();
                    }
           
            
                    
          while(true){
                         
          if(af.isAllFinished()){
                              System.out.println(
          "主線程運(yùn)行結(jié)束,應(yīng)該獲得的最終值為"+threadNum
                                                        
          +";實(shí)際最終值為"+bb);
                          
          break;
                         }

             
                         
          int sleep=(int)(Math.random()*500);
                         
          try {
                              Thread.sleep(sleep);
                         }
           catch (InterruptedException e) {
                              e.printStackTrace();
                         }

                    }
           
          }

          }


          class AtomicIntegerThread extends Thread{
                  
          private int id;
                  
          private AllFinished af;
                  
          public AtomicInteger j;

                   
          public AtomicIntegerThread(AtomicInteger __j,int _id,AllFinished _af) {
                        
          super();
                        
          this.j=__j;
                        
          this.id=_id;
                        
          this.af=_af;
                   }



                   
          public void run() 
                          j.getAndIncrement();
                         af.setItemFinished(id);
                         System.out.println(j); 
          //這里輸出沒有同步,很可能會(huì)出現(xiàn)不一致,這里僅僅用來測試一下,正好能證明線程將操作分解^_^   
                   }

          }



          運(yùn)行了多次沒有出現(xiàn)被分解的情況,最后我把線程數(shù)從一千改成一萬,又增加到十萬,并且運(yùn)行多次,沒有出現(xiàn)結(jié)果和預(yù)計(jì)結(jié)果不一致的情況。十萬個(gè)線程啊!如果能分解的話早把程序分得七零八碎了,呵呵。某次結(jié)果如下:
          ......
          99994
          99993
          99992
          主線程運(yùn)行結(jié)束,應(yīng)該獲得的最終值為100000;實(shí)際最終值為100000
          =====================================================
          【改進(jìn)二】群友TITAN的方法更簡單,在版本一的基礎(chǔ)上加上一小段代碼

          class NoAtomicIntegerThread extends Thread{


                
          public void run() {
                    
          synchronized(jj){
                              jj.int_I
          ++;
                    }

                      af.setItemFinished(id);
                      System.out.println(jj.int_I);
          }



          }



          我對(duì)比了一下改進(jìn)一和改進(jìn)二,在十萬個(gè)線程并發(fā)情況下,二者的運(yùn)行速度幾乎沒有差別。可見JAVA類庫中的AtomicInteger還是很實(shí)用的。

          大伙有什么改進(jìn)的方法,歡迎大家提意見。yuzhe80@126.com

          ******************************************************************************
          后話:這里為了保證主線程在子線程都執(zhí)行完后再繼續(xù)執(zhí)行,特意定義了一個(gè)類AllFinished,記錄每個(gè)子線程是否結(jié)束,其實(shí)大可不必,因?yàn)镴AVA中對(duì)線程有join函數(shù),在main函數(shù)中改成這樣就可以了。

          public class ThreadSafeClass {
                 
          public static void main(String[] args) {
                
          //要?jiǎng)?chuàng)建的子線程數(shù)
                 int threadNum=1000;                          
            
                
          //每個(gè)子線程都要引用的對(duì)象
                 AtomicInteger bb=new AtomicInteger(0);
            
                
          //多個(gè)子線程對(duì)象
                 AtomicIntegerThread[] at=new AtomicIntegerThread[threadNum];
            
                 
          for(int i=0;i<threadNum;i++){
                      at[i]
          =new AtomicIntegerThread(bb,i);
                 }

            
                 
          long last=System.currentTimeMillis();
                 
          for(int i=0;i<threadNum;i++){
                      at[i].start();
                 }
           
            
                 
          for(int i=0;i<threadNum;i++){
                      
          try {
                           at[i].join();                                     
          //保證主線程在所有子線程結(jié)束后再執(zhí)行
                      }
           catch (InterruptedException e) {
                           e.printStackTrace();
                      }

                 }
           
            
                 System.out.println(
          "主線程運(yùn)行結(jié)束,應(yīng)該獲得的最終值為"+threadNum
                 
          +";實(shí)際最終值為"+bb+"程序運(yùn)行時(shí)間"+(System.currentTimeMillis()-last)+"毫秒");

                }

          }



          而且這樣修改,在性能上有很大提升,用我自定義的類AllFinished,運(yùn)行1000個(gè)線程,平均時(shí)間為2100ms,而用join函數(shù),平均時(shí)間為1600ms,性能提升了25%。以后還是推薦大伙使用join吧^_^

          posted on 2007-08-13 16:56 天天看海 閱讀(377) 評(píng)論(0)  編輯  收藏


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


          網(wǎng)站導(dǎo)航:
           
          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          導(dǎo)航

          統(tǒng)計(jì)

          留言簿

          文章檔案

          搜索

          最新評(píng)論

          主站蜘蛛池模板: 蒲江县| 达拉特旗| 泰来县| 壤塘县| 额济纳旗| 高阳县| 焉耆| 石屏县| 建始县| 温州市| 左贡县| 和静县| 商南县| 南阳市| 广宗县| 临清市| 吴桥县| 建宁县| 岚皋县| 芜湖县| 三原县| 贺兰县| 宜春市| 青海省| 普定县| 昔阳县| 盖州市| 白河县| 赣榆县| 武义县| 达拉特旗| 常州市| 新干县| 罗平县| 白玉县| 南皮县| 珠海市| 辉南县| 淮北市| 潢川县| 广灵县|