關於Collection中的安全

來源:互聯網
上載者:User

 

    以前聽老師在講Collection集合架構的時候,說到了Iterator來單線程修改和刪除集合裡面的資料,意思是Iterator來操作刪除和修改集合裡面的資料,感覺也沒什麼關係啊,但是科學的態度永遠的嚴謹的,就在我興奮地時候,問題突如其來:
問題情境: 在Session在事務後立即關閉和Lazy為False,另外查詢快取未顯式開啟的情況下,並且由於hibernate list()在實際情況下是不開啟查詢快取的,Iterator開啟,現在我在此情況下執行一次性查詢載入外鍵對象資料,那麼要顯式的迭代並調用外鍵中的函數,以便產生相應的資料,但是就是在這裡出現了問題,給我拋了一個大大的ConcurrentModificationException,以前我也用過顯式調用來載入外鍵對象資料,但是沒有遇到過這種問題,到底是什麼導致的呢?我現在就這個問題來分析
//以前情境
List<Sightholders> arraySightholders = null;
arraySightholders = session.createQuery(
      "from Sightholders order by sightholdersId asc")
      .setFirstResult(pageSize * (pageNo - 1)).setMaxResults(
        pageSize).list();
    System.out.println("長度" + arraySightholders.size());
    for (Iterator<Sightholders> c = arraySightholders.iterator(); c
      .hasNext();) {
     c.next().getStafferId();
    }

//當前情境  
List sigsCheckInfo = null;
sigsCheckInfo = session.createQuery(sigsHQL)
          .setParameter(0, dateStr)
          .setParameter(1, sigsId)
          .setFirstResult(pageSize * (pageNo - 1))
          .setMaxResults(pageSize)
          .list();
   boolean isScof = false;
   boolean isRcof = false;
  
   for(Iterator iter = sigsCheckInfo.iterator() ;iter.hasNext();){
    Object obj = iter.next();
    
    if((obj instanceof SupplierCheckOutForm) || isScof){
     SupplierCheckOutForm scof = (SupplierCheckOutForm)obj;
     scof.getSupplierId().getSightholdersName();
     scof.getFpreparer().getName();
     isScof= true;
    
    }else if(obj instanceof ReceivingCheckOutForm || isRcof){
     ReceivingCheckOutForm rcof = (ReceivingCheckOutForm)obj;
     rcof.getReceivingId().getSightholdersName();
     rcof.getFpreparer().getName();
     isRcof= true;
    }
    
   }

我們知道在Iterator操作的時候是單線程的,這樣是確保在多客戶使用同一個資料集合的時候避免出現線程不同步的情況,這樣對於效能還是資料安全和資料完整來說都是毀滅性的打擊,那麼我分析了這兩段代碼本質上沒什麼不同,但是我們可以看到一個明顯的區別,那就是泛型的引入,在情境一中是指定了泛型的,而情境二沒有,這樣就導致了我在情境二需要存入可能是不同的對象,當我們以Object類型來讀取出資料庫中的資料後,並沒有指定相應的資料類型,從而導致在Iterator操作的時候出現了修改資料類型的一種操作,從Object脫離到可能兩種的對象,所以這個時候出現了修改,這個時候就會威脅到Iterator 本質的安全,所以給了我一個大大的 ConcurrentModificationException
我們來分析下這個異常:java.util.ConcurrentModificationException
導致拋出此異常的原因是:對集合類進行迭代的時候對裡面的內容進行了修改,即不能用他們的Api來修改集合裡面的內容,這樣會導致出現這樣的問題
下面我對情境二代碼做了如下修改,一切正常:
    if((obj instanceof SupplierCheckOutForm) || isScof){
     SupplierCheckOutForm scof = (SupplierCheckOutForm)obj;
     scof.getSupplierId().getSightholdersName();
     scof.getFpreparer().getName();
     sigsCheckInfo.remove(obj);
     sigsCheckInfo.add(scof);
    
//     iter.remove();
     isScof= true;
    
    }else if(obj instanceof ReceivingCheckOutForm || isRcof){
     ReceivingCheckOutForm rcof = (ReceivingCheckOutForm)obj;
     rcof.getReceivingId().getSightholdersName();
     rcof.getFpreparer().getName();
     sigsCheckInfo.remove(obj);
     sigsCheckInfo.add(rcof);
//     iter.remove();
     isRcof= true;
    }
注意在紅色類的對象RCOF 和SCOF中,需要添加HasCode和Equals方法,否則會導致刪除不了
也可以使用如下的修改:
   for(Iterator iter = sigsCheckInfo2.iterator() ;iter.hasNext();){
    Object obj = iter.next();
    
    if((obj instanceof SupplierCheckOutForm) || isScof){
     SupplierCheckOutForm scof = (SupplierCheckOutForm)obj;
     scof.getSupplierId().getSightholdersName();
     scof.getFpreparer().getName();
     sigsCheckInfo.add(scof);
     iter.remove();
     isScof= true;
    
    }else if(obj instanceof ReceivingCheckOutForm || isRcof){
     ReceivingCheckOutForm rcof = (ReceivingCheckOutForm)obj;
     rcof.getReceivingId().getSightholdersName();
     rcof.getFpreparer().getName();
     sigsCheckInfo.add(rcof);
     iter.remove();
     isRcof= true;
    }
    
   }  
在此我用了兩個List來儲存和轉換,也相當於資料的相互轉移,當然這裡返回的最好是LinkedList,因為鏈表的操作增加刪除快,然後通過Iterator的安全remove操作,對原始的Iterator對象進行抽取.
當前在另外的情境中也有出現此問題的,例如下面的這個例子:
public void setReparation(Reparation reparation) {
  for (Iterator it = this.reparations.iterator(); it.hasNext();) {
   Reparation repa = (Reparation) it.next();
   if (repa.getId() == reparation.getId()) {
    this.reparations.remove(repa);
    this.reparations.add(reparation);
   }
  }
}注意在此代碼中Reparation reparation 為集合類,例如ArrayList,等,由於在Iterator中對Arraylist進行了remove操作,所以我們發現報了異常,可以採取下列方式來進行修改:
public void setReparation(Reparation reparation) {
  boolean flag = false;
  for (Iterator it = this.reparations.iterator(); it.hasNext();) { //reparations為Collection  
   Reparation repa = (Reparation) it.next();
   if (repa.getId() == reparation.getId()) {
    it.remove();
    flag = true;
    break;
   }
  }
  if (flag) {
   this.reparations.add(reparation);
  }
}

綜上所述:我們在編程的時候尤其是在對Iterator中的資料進行對集合類中資料進行刪除操作的時候可以再Iterator中進行,但是關於修改操作,我們要換中方式在 Iterator中變相進行修改,養成良好的多線程變成習慣,將代碼危險防範於未然.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.