天天看海

          每天都想看海

          一個能夠證明i++在多線程下非原子化的例子及其改進

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

          【版本一】

          import cn.yuzhe.multi.util.AllFinished;

          public class ThreadSafeClass {

          public static void main(String[] args) {
                   
          //要創建的子線程數
                       int threadNum=1000;                          
            
                   
          //每個子線程都要引用的對象
                       A_Integer bb=new A_Integer();
            
                  
          //創建能判斷所有子線程是否結束的對象,用于保證主線程能在多有子線程都結束后再執行余下的代碼,
                       
          //防止主線程提前于子線程結束
                       AllFinished af=new AllFinished(threadNum);  
            
                   
          //創建多個子線程對象
                       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(
          "主線程運行結束,應該獲得的最終值為"+threadNum
                                                    
          +";實際最終值為"+bb.int_I);
                                   
          break;
                             }

             
                            
          int sleep=500;    //睡眠一段時間,釋放時間片
                            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);              //輸出中間結果
          }


          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;
          }



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

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

          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;
                      }

          }


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

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

          public class ThreadSafeClass {

                    
          public static void main(String[] args) {
                
          //要創建的子線程數
                    int threadNum=1000;                          
            
                
          //每個子線程都要引用的對象
                    AtomicInteger bb=new AtomicInteger(0);
            
                
          //創建能判斷所有子線程是否結束的對象,用于保證主線程能在多有子線程都結束后再執行余下的代碼,
                    
          //防止主線程提前于子線程結束
                    AllFinished af=new AllFinished(threadNum);  
            
                
          //多個子線程對象
                    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(
          "主線程運行結束,應該獲得的最終值為"+threadNum
                                                        
          +";實際最終值為"+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); 
          //這里輸出沒有同步,很可能會出現不一致,這里僅僅用來測試一下,正好能證明線程將操作分解^_^   
                   }

          }



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

          class NoAtomicIntegerThread extends Thread{


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

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



          }



          我對比了一下改進一和改進二,在十萬個線程并發情況下,二者的運行速度幾乎沒有差別。可見JAVA類庫中的AtomicInteger還是很實用的。

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

          ******************************************************************************
          后話:這里為了保證主線程在子線程都執行完后再繼續執行,特意定義了一個類AllFinished,記錄每個子線程是否結束,其實大可不必,因為JAVA中對線程有join函數,在main函數中改成這樣就可以了。

          public class ThreadSafeClass {
                 
          public static void main(String[] args) {
                
          //要創建的子線程數
                 int threadNum=1000;                          
            
                
          //每個子線程都要引用的對象
                 AtomicInteger bb=new AtomicInteger(0);
            
                
          //多個子線程對象
                 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();                                     
          //保證主線程在所有子線程結束后再執行
                      }
           catch (InterruptedException e) {
                           e.printStackTrace();
                      }

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

                }

          }



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

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


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


          網站導航:
           
          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          導航

          統計

          留言簿

          文章檔案

          搜索

          最新評論

          主站蜘蛛池模板: 防城港市| 富顺县| 平和县| 景德镇市| 乌拉特中旗| 汕尾市| 南安市| 平罗县| 杭锦后旗| 醴陵市| 达州市| 景洪市| 石屏县| 富蕴县| 唐海县| 花垣县| 内江市| 鹤峰县| 政和县| 宁安市| 霸州市| 健康| 四平市| 哈巴河县| 九龙城区| 黄冈市| 龙游县| 庆阳市| 新乡县| 天长市| 麻阳| 商城县| 古交市| 新田县| 襄垣县| 江阴市| 金坛市| 霍山县| 鹰潭市| 樟树市| 涡阳县|