为什么notifyAll()没有唤醒此示例中的所有线程?

时间:2016-04-03 01:14:54

标签: java multithreading wait notify

我正在试图弄清楚如何使用等待和放大通知,所以我写了这个小例子,有几架飞机等待跑道在起飞前清除,我遇到的问题是当飞机起飞,并且调用notifyAll()时,只有一个线程似乎被唤醒,即我希望所有线程报告他们已被通知,但仍在等待。实际发生的是只有一个线程被唤醒,其余线程什么都不做。为什么看起来只有一个线程被唤醒,我该如何解决呢?

class Plane extends Thread
{
    Runway runway;

    Plane(int id, Runway runway)
    {
        super(id + "");
        this.runway = runway;

    }

    public void run()
    {
        runway.taxi();
        runway.takeoff();
    }
}

class Runway
{
    boolean isFull;

    Runway()
    {
        isFull = false;;
    }

    public synchronized void taxi()
    {
        System.out.println(Thread.currentThread().getName() + " started to taxi");
        while(isFull)
        {
            System.out.println(Thread.currentThread().getName() + " is queued");
            try
            {
                wait();
            }
            catch(InterruptedException e){}     
        }
        isFull = true;
        System.out.println(Thread.currentThread().getName() + " entering runway");
    }

    public synchronized void takeoff()
    {
        try
        {
            Thread.currentThread().sleep(1000);
        }
        catch(InterruptedException e){}
        System.out.println(Thread.currentThread().getName() + " took off");
        isFull = false;
        notifyAll();
    }

    public static void main(String[] args)
    {
        Runway runway = new Runway();
        new Plane(1, runway).start();
        new Plane(2, runway).start();
        new Plane(3, runway).start();
        new Plane(4, runway).start();
    }
}

感谢您抽出宝贵时间帮助我:)

3 个答案:

答案 0 :(得分:3)

因为notifyAll()不是wakeAll()。所有线程都会得到通知,但只有一个线程会获取该键并且正在运行。所有其他人再次等待拉。

答案 1 :(得分:1)

这就是它的作用。它“通知”所有等待的线程,但只有一个唤醒并获得CPU。 notify()根据底层线程实现选择的内容选择一个等待线程。 notifyAll()为所有等待的线程提供平等的竞争机会。但无论哪种方式,只有一个线程采用上下文。

答案 2 :(得分:1)

假设您有4个平面全部start() - 依次编辑。

所有4个人都会尝试拨打taxi(),然后拨打takeoff()

第一个会拨打taxi()

  • 获得锁,
  • 查找isFullfalse
  • isFull设为true
  • 返回,释放锁

然后一个(或多个)剩余线程可以调用taxi()。如果他们这样做,他们:

  • 获得锁定
  • 查找isFullfalse
  • 调用释放锁的wait()

OR

  • 在尝试获取锁定时阻止

同时,从taxi()返回的主题将调用takeoff()。这将:

  • 获得锁定
  • 睡1秒钟,
  • 通知正在等待的所有线程
  • 返回,释放锁。

那么这是如何解释你所看到的?

假设当从taxi()返回的第一个线程时,它立即能够重新获取锁并启动takeoff()调用。然后在握住锁定时调用sleep()。这可以防止任何其他线程启动taxi()调用(如果他们还没有这样做)。然后在睡眠之后,它会调用notifyAll()。但这只会通知已进入taxi()调用且已调用wait()的线程。在启动taxi()调用时被阻止的任何线程都不会看到通知。

(对于不在wait()调用中的线程,通知永远不会排队。)

这可能吗?嗯,是的。

启动一个线程是一个相对昂贵/耗时的过程,并且很有可能第一个线程启动将在下一个线程启动之前完成大量工作。有可能在第二个人试图呼叫sleep之前,它会一直接到taxi()来电。

对于剩余的线程,可能会重复相同的模式。进入taxi()的每个线程可能会释放,然后在调度另一个线程之前重新获取它。 (线程调度由操作系统处理,并且它针对效率而不是公平进行优化。如果您想要公平调度,则需要使用Lock对象。)

  

......如何解决?

更改您的代码,以便在按住锁定时不会sleep。例如:

public void takeoff() {
    try {
        Thread.currentThread().sleep(1000);
    } catch (InterruptedException e) {
        // squash ...
    }
    System.out.println(Thread.currentThread().getName() + " took off");
    synchronize (this) {
        isFull = false;
        notifyAll();
    }
}