java的wait()和notify()是不可靠的,尽管已同步?
我最近发现,使用synchronized不会阻止任何死锁
例如,在本规范中:
ArrayList <Job> task;
...
public void do(Job job){
synchronized(tasks){
tasks.add(job);
}
synchronized(this){
notify();
}
}
public void run(){
while(true){
for (int = 0;i<tasks.size();i++){
synchronized(tasks){
Job job = tasks.get(i);
}
//do some job here...
}
synchronized(this){
wait(); //lock will be lost...
notifier = false; //lock will be acquired again after notify()
}
}
}
现在,问题是什么?如果正在运行的线程没有等待,他将看不到任何通知(即notify()调用),因此他可能会遇到死锁,无法处理他收到的任务!(或者他可能处理得太晚了……)
因此,我实现了以下代码:
private volatile boolean notifier = false;
ArrayList <Job> task;
...
public void do(Job job){
synchronized(tasks){
tasks.add(job);
}
synchronized(this){
notifier = true;
notify();
}
}
public void run(){
while(true){
for (int = 0;i<tasks.size();i++){
synchronized(tasks){
Job job = tasks.get(i);
}
//do some job here...
}
synchronized(this){
if(!notifier){
wait(); //lock will be lost...
notifier = false; //lock will be acquired again after notify()
}
}
}
}
这是正确的还是我遗漏了什么?能做得更容易些吗
# 1 楼答案
一开始我被你的问题骗了。 你的线程同步对象没有意义。我过去也做过这样的事情,使wait()不会抛出编译错误
只有当您正在等待并希望获取这些资源时,同步(任务)才有意义
有一个for循环,这是一个糟糕的设计。消费者-生产者问题。在删除作业的同时获取作业。最好一次找一份工作
# 2 楼答案
对。这不是“不可靠”的情况,而是语言定义的情况。
notify()
调用不会将通知排队。如果没有线程在等待,那么notify()
实际上什么也不做对。我会考虑使用} 应该适合你。一个线程调用从队列中拉出,另一个可以添加到队列中。它将为您提供锁定和信号。一旦你开始使用手写代码,你应该能够删除它的很大一部分
BlockingQueue
一个^{# 3 楼答案
结果实际上并不是死锁,而是任务/工作本身的饥饿。因为没有线程被“锁定”,所以在另一个线程调用
do(Job job)
之前,任务不会完成除了调用
wait()
和notify()
时缺少的异常处理之外,您的代码几乎是正确的。但是,您可以将task.size()
放在同步块中,也可以在hole进程中阻止任务,因为另一个线程删除任务中的作业会导致循环失败:只需注意你的代码是阻塞的。非阻塞可能更快,如下所示:
我们能学到什么?在非阻塞线程中,不需要
notify()
、wait()
和synchronized
。设置wait(1)在空闲时甚至不会使用更多的CPU(不要设置wait(0),因为这将被视为wait()但是,要小心,因为使用
wait(1)
可能比使用wait()
和notify()
:Is wait(1) in a non-blocking while(true)-loop more efficient than using wait() and notify()?慢(换句话说:非阻塞可能比阻塞慢!)