为什么 notifyAll() 没有唤醒这个例子中的所有线程?
Why is notifyAll() not waking all of the threads in this example?
我正在尝试弄清楚如何使用等待和通知,所以我写了这个小例子,其中有几架飞机在起飞前等待跑道清理,我遇到的问题是当一架飞机起飞并调用 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();
}
}
感谢您花时间帮助我:)
因为 notifyAll() 不是 wakeAll()。所有线程都会收到通知,但只有一个线程掌握了密钥并且正在 运行。所有其他人再次等待拉动。
这就是它的作用。它 "notifies" 所有等待的线程,但只有一个唤醒并获得 CPU。 notify()
picks a waiting thread based on what the underlying thread implementation selects. notifyAll()
给所有等待线程一个平等的竞争机会。但无论哪种方式,只有一个线程获取上下文。
假设你有 4 个飞机,它们都是 start()
一个接一个地编辑的。
所有 4 个人都将尝试呼叫 taxi()
,然后是 takeoff()
第一个会调用 taxi()
:
- 获取锁,
- 发现
isFull
是false
- 将
isFull
设置为true
- return,释放锁
然后一个(或多个)剩余线程可能会调用 taxi()
。如果他们这样做,他们:
- 获取锁
- 发现
isFull
是false
- 调用
wait()
释放锁
或
- 尝试获取锁时阻塞
与此同时,return从 taxi()
编辑的线程将调用 takeoff()
。这将:
- 获取锁
- 睡眠1秒,
- 通知任何正在等待的线程
- return,释放锁
那么这如何解释您所看到的?
假设当第一个线程从 taxi()
中 return 时,它立即能够重新获取锁并启动 takeoff()
调用。然后它会在持有锁的同时调用 sleep()
。这将阻止任何其他线程开始它们的 taxi()
调用(如果它们还没有这样做的话)。然后在睡眠之后,它会调用notifyAll()
。但这只会通知已进入 taxi()
调用和已调用 wait()
的线程。在启动 taxi()
调用时被阻塞的任何线程将永远不会看到通知。
(通知永远不会为不在 wait()
调用中的线程排队。)
这可能吗?嗯,是的。
启动线程是一个相对昂贵/耗时的过程,第一个启动的线程很可能会在下一个线程启动之前完成大量工作。有可能它会在第二个尝试调用 taxi()
.
之前一直到达 sleep
调用
其余线程可能会重复相同的模式。当进入 taxi()
的每个线程都可能释放并在另一个线程被调度之前重新获取它。 (线程调度由OS处理,它是为了效率而不是公平进行优化。如果要公平调度,则需要使用Lock
对象。)
... how can a fix it?
更改您的密码,这样您就不会 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();
}
}
我正在尝试弄清楚如何使用等待和通知,所以我写了这个小例子,其中有几架飞机在起飞前等待跑道清理,我遇到的问题是当一架飞机起飞并调用 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();
}
}
感谢您花时间帮助我:)
因为 notifyAll() 不是 wakeAll()。所有线程都会收到通知,但只有一个线程掌握了密钥并且正在 运行。所有其他人再次等待拉动。
这就是它的作用。它 "notifies" 所有等待的线程,但只有一个唤醒并获得 CPU。 notify()
picks a waiting thread based on what the underlying thread implementation selects. notifyAll()
给所有等待线程一个平等的竞争机会。但无论哪种方式,只有一个线程获取上下文。
假设你有 4 个飞机,它们都是 start()
一个接一个地编辑的。
所有 4 个人都将尝试呼叫 taxi()
,然后是 takeoff()
第一个会调用 taxi()
:
- 获取锁,
- 发现
isFull
是false
- 将
isFull
设置为true
- return,释放锁
然后一个(或多个)剩余线程可能会调用 taxi()
。如果他们这样做,他们:
- 获取锁
- 发现
isFull
是false
- 调用
wait()
释放锁
或
- 尝试获取锁时阻塞
与此同时,return从 taxi()
编辑的线程将调用 takeoff()
。这将:
- 获取锁
- 睡眠1秒,
- 通知任何正在等待的线程
- return,释放锁
那么这如何解释您所看到的?
假设当第一个线程从 taxi()
中 return 时,它立即能够重新获取锁并启动 takeoff()
调用。然后它会在持有锁的同时调用 sleep()
。这将阻止任何其他线程开始它们的 taxi()
调用(如果它们还没有这样做的话)。然后在睡眠之后,它会调用notifyAll()
。但这只会通知已进入 taxi()
调用和已调用 wait()
的线程。在启动 taxi()
调用时被阻塞的任何线程将永远不会看到通知。
(通知永远不会为不在 wait()
调用中的线程排队。)
这可能吗?嗯,是的。
启动线程是一个相对昂贵/耗时的过程,第一个启动的线程很可能会在下一个线程启动之前完成大量工作。有可能它会在第二个尝试调用 taxi()
.
sleep
调用
其余线程可能会重复相同的模式。当进入 taxi()
的每个线程都可能释放并在另一个线程被调度之前重新获取它。 (线程调度由OS处理,它是为了效率而不是公平进行优化。如果要公平调度,则需要使用Lock
对象。)
... how can a fix it?
更改您的密码,这样您就不会 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();
}
}