Java 尽管已同步,wait()和notify()是否不可靠?
我最近发现,使用synchronized不会阻止任何死锁 例如,在本规范中:Java 尽管已同步,wait()和notify()是否不可靠?,java,multithreading,wait,synchronized,notify,Java,Multithreading,Wait,Synchronized,Notify,我最近发现,使用synchronized不会阻止任何死锁 例如,在本规范中: ArrayList <Job> task; ... public void do(Job job){ synchronized(tasks){ tasks.add(job); } synchronized(this){ notify(); } } public void run(){ while(true){ for
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()
}
}
}
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()
}
}
}
}
现在,问题是什么?嗯,如果正在运行的线程没有等待,他将看不到任何通知,即notify调用,因此他可能会遇到死锁,无法处理收到的任务!或者他可能处理得太晚了
因此,我实现了以下代码:
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()
}
}
}
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()
}
}
}
}
这是正确的还是我遗漏了什么?能做得更容易些吗
现在,问题是什么?嗯,如果正在运行的线程没有等待,他将看不到任何通知,即notify调用,因此他可能会遇到死锁,无法处理收到的任务
对。这不是不可靠的情况,而是语言定义的情况。notify调用不会将通知排队。如果没有线程在等待,那么notify实际上什么也不做
能做得容易些吗
对。我会考虑使用BlockingQueue-a,这对你来说应该很好。一个线程调用从队列中拉出,另一个线程调用可以添加到队列中。它将为您提供锁定和信号。一旦您开始使用手写代码,您应该能够删除大部分手写代码。一开始我被您的问题欺骗了。 线程对象上的同步没有意义。我过去也做过这件事,使等待不抛出编译错误 只有同步任务才有意义,因为您正在等待并希望获得这些资源 有一个for循环,这是一个糟糕的设计。消费者-生产者问题。在删除作业的同时获取作业。最好一次找一份工作
public void do(Job job){
synchronized(tasks){
tasks.add(job);
notify();
}
}
public void run(){
Job job;
while(true){
//This loop will fetch the task or wait for task notification and fetch again.
while (true){
synchronized(tasks){
if(tasks.size()>0){
job = tasks.getTask(0);
break;
}
else
wait();
}
}
//do some job here...
}
}
结果实际上并不是死锁,而是任务/作业本身的匮乏。因为没有线程被锁定,所以在另一个线程调用doJob作业之前,任务不会完成 除了调用wait和notify时丢失的异常处理之外,您的代码几乎是正确的。但您可以将task.size放在同步块中,也可以在钻孔过程中阻止任务,因为另一个线程删除任务中的作业会导致循环失败:
...
while(true){
synchronized(tasks){
for (int = 0;i<tasks.size();i++){ //could be done without synchronisation
Job job = tasks.get(i); //if noone deletes any tasks
}
//do some job here...
}
...
请注意,您的代码是阻塞的。非阻塞可能更快,如下所示:
ArrayList <Job> tasks;
...
public void do(Job job){
synchronized(tasks){
tasks.add(job);
}
}
public void run(){
while(true){
int length;
synchronized(tasks){
length = tasks.size();
}
for (int = 0;i<length;i++){
Job job = tasks.get(i); //can be done without synchronisation if noone deletes any tasks...otherwise it must be within a synchronized block
//do some job here...
}
wait(1); //wait is necessary and time can be set higher but never 0!
}
}
我们能学到什么?在非阻塞线程中,不需要通知、等待和同步。设置wait1甚至不会在空闲时使用更多CPU。不要设置wait0,因为这将被视为等待
但是,要小心,因为使用wait1可能比使用wait和notify慢:换句话说,非阻塞可能比阻塞慢 编写线程安全代码很难。你应该使用现有的阻塞队列或只是执行器,而不是重新发明轮子。我倾向于同意。对于这样的任务列表,请使用已经实现的某种线程安全队列。也许这个:嗯,重新发明轮子更像是创造一种新的编程语言。所以,不,我不会重新发明轮子,因为编写代码更像是设计汽车,而不是发明轮子。不过,我喜欢法拉利而不是标致,法拉利追求的是速度和简洁;BlockingQueue是一种更有效、更好的模式@Marcus IMO。你至少应该了解它们。谢谢。我这么说不仅仅是为了得到分数,马库斯-+1感谢您认识到这是生产者/消费者的问题。如果您不喜欢此评论,我很抱歉,但wait100更像是热修复代码,而不是提供实际答案。他也可以使用信号灯或监视器来解决此问题。一次一步。了解如何使用wait和notify以及它的缺点,因此他会寻找其他更好的解决方案。你能仔细解释一下为什么wait100中的wait0吗?同意,非阻塞线程应该是wait0,或者类似wait5的东西……但有趣的解决方案,while循环中的while循环;修订过的。这应该是调用wait和notify的方式,有关同步的最佳教程之一可以在此处找到: