0

我有一段代碼來創建一個對象並增加創建對象的數量。創建對象和增加計數的代碼,如果條件檢查計數是否達到了最大數量的對象,則通過if條件進行檢查。當使用多線程運行代碼時,if條件可能被違反(即無效)由行的位置決定。這可能是由Java編譯器優化造成的嗎?以下是代碼片段:Java編譯器代碼優化違反條件?

private BlockingQueue<WeakReference<ItemType>> items; 
private final ReferenceQueue<ItemType> referenceQueue = new ReferenceQueue<ItemType>(); 
private final int maxSize = 10; 
private final AtomicInteger numberOfCreatedObj = new AtomicInteger(0); 

protected ItemType create(boolean addToCache) { 
ItemType item = null; 
try {   
    if (!hasCreatedMaxObjects()) { 
     // we have not reached maxSize yet. 
     item = getFactory().create(); 
     // this position makes the MAX objects checking working 
        // Position A: 
     increaseCreatedObjectCount(); 
     LOG.debug("Created new item [" + item.getId() + "]"); 
     if (addToCache) { 
      LOG.debug("Add to cache the new item [" + item.getId() + "]"); 
      addCreated(item); 
     } 
     // This position makes the MAX objects checking failed 
        // Position B;  
     //increaseCreatedObjectCount();    
    } else { 
     LOG.warn("Already reached MAX created objects " + numberOfCreatedObj.get()); 
    } 
} catch (Exception e) { 
    LOG.error("Error in creating a new object", e); 
} 
return item; 
} 

protected boolean hasCreatedMaxObjects() {  
return getNumberOfCreatedObj().compareAndSet(getMaxSize(), getMaxSize()); 
} 

protected void increaseCreatedObjectCount() { 
getNumberOfCreatedObj().incrementAndGet(); 
} 

protected void addCreated(ItemType item) { 
    items.offer(new WeakReference<ItemType>(item, referenceQueue)); 
} 

我用30個線程運行了我的測試。獲得創建的對象後,每個線程休眠100毫秒。當在位置A調用increaseCreatedObjectCount()時,代碼工作正常,並且創建了10個(MAX)對象。當在位置B調用increaseCreatedObjectCount()時,創建了30個對象,這等於正在運行的線程的數量。

如何查看Java編譯器優化代碼?

謝謝。

+0

預期結果是什麼,實際結果是什麼?目前還不清楚問題是什麼。 –

+0

這是什麼*問題* – EJP

+0

預期結果:只創建不超過最大尺寸的對象。 – qwang

回答

3

我不知道我完全理解這個問題,但什麼是肯定是,你必須在你的代碼中的競爭條件,因爲你的代碼基本上沒有

if (count < max) { 
    count++; 

如果兩個線程先檢查,並行,如果已達到最大值,然後並行增加計數,那麼當然最大值將執行兩次,即使您只剩下一個時隙:

  • 線程A,校驗值,值= 9,如果區塊爲
  • 線程B,檢查值,值= 9,進入if塊
  • 線程A,增量,值= 10
  • 線程B,增量,值= 11

而且我並不感到驚訝它發生當增量位於位置B而不是位置A時,由於計數仍未增加的時間段較長,因此另一個線程可能會進入if塊。爲了使其更清楚,在位置B處增量的代碼可與

if (count < max) { 
    sleep(ENOUGH_TIME_FOR_ANOTHER_THREAD_TO_STILL_SEE_THE_NON_INCREMENTED_VALUE) 
    count++; 

編譯器優化因此與該問題無關。問題在於缺乏同步,或者缺乏AtomicInteger的正確使用。 IF塊應該是

if (numberOfCreatedObj.getAndIncrement() < maxSize) { 
    ... 
} 

另外請注意,即使您的測試表明,它按預期工作時的增量在位置A,這只是一個意外。在位置A的增量可能會發生同樣的錯誤。您只是幸運地不會看到它發生。

+1

+1注意:'javac'編譯器幾乎沒有優化。它是優化代碼的JIT,除非你有錯誤,否則你應該能夠計算出允許哪些優化。例如在AtomicXxxx方面哪個方面很少。 –

+0

謝謝你的答覆。我同意這是一個競爭條件。 – qwang

+0

「numberOfCreatedObj.getAndIncrement() qwang