有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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()
            }
        }
    }
}

这是正确的还是我遗漏了什么?能做得更容易些吗


共 (3) 个答案

  1. # 1 楼答案

    一开始我被你的问题骗了。 你的线程同步对象没有意义。我过去也做过这样的事情,使wait()不会抛出编译错误

    只有当您正在等待并希望获取这些资源时,同步(任务)才有意义

    有一个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...
    
        }
    }
    
  2. # 2 楼答案

    Now, what is the problem? Well, if the running thread isn't waiting, he won't see any notifications (i.e. notify() calls), therefore he may run into a dead lock and not handle the tasks he received!

    对。这不是“不可靠”的情况,而是语言定义的情况。notify()调用不会将通知排队。如果没有线程在等待,那么notify()实际上什么也不做

    can it be done easier?

    对。我会考虑使用BlockingQueue一个^{}应该适合你。一个线程调用从队列中拉出,另一个可以添加到队列中。它将为您提供锁定和信号。一旦你开始使用手写代码,你应该能够删除它的很大一部分

  3. # 3 楼答案

    结果实际上并不是死锁,而是任务/工作本身的饥饿。因为没有线程被“锁定”,所以在另一个线程调用do(Job job)之前,任务不会完成

    除了调用wait()notify()时缺少的异常处理之外,您的代码几乎是正确的。但是,您可以将task.size()放在同步块中,也可以在hole进程中阻止任务,因为另一个线程删除任务中的作业会导致循环失败:

    ...
    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!
        }
    }
    

    我们能学到什么?在非阻塞线程中,不需要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()?慢(换句话说:非阻塞可能比阻塞慢!)