vtortola
vtortola

Reputation: 35945

Object pooling with .wait and .notify

I'm trying to create a class in java that pool objects. The class starts creating the minimum amount of objects required, when the requests start to kick in, every thread checks if there a object available, if it can create it because the maximum has not been reached yet, or if otherwise it has to wait to get one.

The idea is the threads needs to synchronize to get/create an engine, but they can process in parallel (ProcessWithEngine method). The processing could take a couple of minutes, and apparently it's working as I want.

The problem is, that sometimes when .notify() has been called and a thread is released from the .wait(), the queue has 0 items, and that should be impossible because just before .notify(), an item is added.

What could be the problem?

The code is like this:

Queue _queue = new Queue();

int _poolMax = 4;
int _poolMin = 1;
int _poolCurrent =0;


public void Process(Object[] parameters) throws Exception
{
    Engine engine = null;

    synchronized(_queue) 
    {
        if(_queue.isEmpty() && _poolCurrent >= _poolMax)
        { 
            _queue.wait();

            // HERE : sometimes _queue.isEmpty() is true at this point.

            engine = (SpreadsheetEngine)_queue.dequeue();

        }
        else if (_queue.isEmpty() && _poolCurrent < _poolMax)
        {               
            engine = CreateEngine();
            _poolCurrent++;
        }
        else
        {               
            engine = (Engine)_queue.dequeue();
        }   
    }

    ProcessWithEngine(engine, parameters);


    // work done
    synchronized(_queue) 
    {
        _queue.enqueue(engine);

        _queue.notify();
    }
}

I've fixed it doing this:

            do
            {
                _queue.wait();

            }
            while(_queue.isEmpty());

But basically this means that a thread is losing its turn, and it could mean a timeout later on.

Upvotes: 0

Views: 872

Answers (3)

axtavt
axtavt

Reputation: 242726

It's possible for at least two reasons:

  • Spurious wakeups, as noted by Justin Waugh
  • Another thread acquires a lock and calls dequeue() in between - after the first thread called notify() and finished its synchornized block, but before the second thread actually woke up from its wait(). It's possible because synchronized/wait()/notify() doesn't guarantee fairness.

Therefore wait() should be always used inside a loop:

while (_queue.isEmpty())
    _queue.wait();

Upvotes: 1

forty-two
forty-two

Reputation: 12817

With you solution, doesn't every thread enter an endless wait()?

The normal idiom is something like this:

synchronized(stuff) {
  while (mustWait) 
     wait();
  // do things with stuff
}

On the other hand, since you're already using a Queue, why not make it a java.util.concurrent.BlockingQueue and get a concurrency solution for free?

Upvotes: 1

Justin Waugh
Justin Waugh

Reputation: 4163

All calls to .wait() have to be enclosed in a while loop. Calls to wait() can just randomly wake up.

Per the documentation: "As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop:"

Upvotes: 3

Related Questions