Reputation: 35945
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
Reputation: 242726
It's possible for at least two reasons:
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
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
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