0

I have a piece of code to create an object and increase the count of created objects. The code of creating object and increasing the count in guarded by an if condition checking if the count has reached the MAX number of objects. When running the code with multi-threads, the if condition could be breached (i.e. no effect) determined by the position of the line increment the count. Could this caused by Java compiler optimization? The following is the code snippets:

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

I ran my test with 30 thread. Each thread sleeps for 100 milliseconds after getting a created object. When increaseCreatedObjectCount() is called at position A, the code work fine and there were 10 (MAX) objects created. When increaseCreatedObjectCount() is called at position B, 30 objects were created, which equals to the number of running threads.

How could I view Java compiler optimized code?

Thank you.

4
  • What is the expected result, and what is the actual result? It's unclear what the problem is. Commented Jul 14, 2013 at 17:36
  • What's the question here? Commented Jul 14, 2013 at 23:06
  • Expected result: Only created objects not exceeding max size. Commented Jul 15, 2013 at 4:59
  • Actual result:When increaseCreatedObjectCount() is called at position A, the code work fine and there were 10 (MAX) objects created. When increaseCreatedObjectCount() is called at position B, 30 objects were created, which equals to the number of running threads. Commented Jul 15, 2013 at 5:00

1 Answer 1

3

I'm not sure that I fully understand the problem, but what's sure is that you have a race condition in your code, since your code basically does

if (count < max) {
    count++;

If two threads first check, in parallel, if the max has been reached, and then, in parallel, increment the count, then of course the max value will be implemented twice even if you have only one slot left:

  • thread A, check value, value = 9, enter if block
  • thread B, check value, value = 9, enter if block
  • thread A, increment, value = 10
  • thread B, increment, value = 11

And I'm not surprised it happens when the increment is at position B rather than at position A, since there is a much longer time-frame during which the count has still not be incremented, and another thread might thus enter the if block. To make it clearer, the code with the increment at position B is comparable with

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

Compiler optimizations thus have nothing to do with the problem. The problem is the lack of synchronization, or the lack of correct usage of AtomicInteger. The if block should be

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

Also, note that even if your test shows that it works as expected when the increment is at position A, that's only an accident. The same bug could happen with the increment at position A. You were just lucky enough not to see it happen.

Sign up to request clarification or add additional context in comments.

6 Comments

+1 Note: the javac compiler does almost no optimisations. It is the JIT which optimises the code, and unless you have a bug you should be able to work out what optimisations are allowed. e.g. in terms of AtomicXxxx very few which matter.
Thank you for the replies. I agree that is a race condition.
"numberOfCreatedObj.getAndIncrement() < maxSize" is worse in such kind of race condition.
@qwang: why? the increment is now atomic, and there's no chance for two threads to ever see the same number. Or I'm missing something?
Sorry for not being specific. I said it is worse because numberOfCreatedObjects will be out of sync with the actual number of created objects when the if condition is tested after reached max size.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.