我正在尝试在java中创建一个用于池对象的类。该类开始创建所需的最少数量的对象,当请求开始启动时,每个线程检查是否有可用对象,是否可以创建它因为尚未达到最大值,或者否则它必须等待得到一个。
这个想法是线程需要同步来获取/创建引擎,但它们可以并行处理(ProcessWithEngine
方法)。处理可能需要几分钟,显然它正在按我的意愿工作。
问题是,有时当调用.notify()
并且从.wait()
释放一个线程时,队列有0个项目,这应该是不可能的,因为在.notify()
之前,添加了一个项目。
可能是什么问题?
代码是这样的:
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();
}
}
我修复它这样做:
do
{
_queue.wait();
}
while(_queue.isEmpty());
但基本上这意味着一个线程正在失去它,这可能意味着稍后会超时。
答案 0 :(得分:3)
对.wait()
的所有调用都必须包含在while
循环中。致wait()
的电话可以随便唤醒。
根据documentation:“在一个参数版本中,中断和虚假唤醒是可能的,并且此方法应始终在循环中使用:”
答案 1 :(得分:1)
有了你的解决方案,并不是每个线程都进入无休止的等待()?
正常的习语是这样的:
synchronized(stuff) {
while (mustWait)
wait();
// do things with stuff
}
另一方面,由于您已经在使用Queue,为什么不将其设为java.util.concurrent.BlockingQueue
并免费获得并发解决方案?
答案 2 :(得分:1)
至少有两个原因:
dequeue()
- 在第一个线程调用notify()
并完成其synchornized
块之后,但在第二个线程实际从其{{{0}醒来之前1}}。这可能是因为wait()
/ synchronized
/ wait()
无法保证公平。因此,notify()
应始终在循环中使用:
wait()