gpt4 book ai didi

java - 使用同步但结果 IllegalMonitorStateException 从另一个类通知线程

转载 作者:塔克拉玛干 更新时间:2023-11-02 08:05:16 26 4
gpt4 key购买 nike

我想通过多线程模拟赛跑游戏,我希望运行者在Referee 开枪后开始运行,所以我把wait()放在运行者的run方法等待Referee,这是runner(PrintRunner.class)run方法

@Override
public void run() {
sleepTime = random.nextInt(3)+8;
System.out.println(name+" is ready~");
try {
synchronized(this){
wait();
}
System.out.println("Start running~");
Thread.sleep(sleepTime*1000);
} catch (InterruptedException e) {
System.err.println(e.getMessage());
}
System.out.println(name +" win the game!!!");
}

这是Referee运行方法:

@Override
public void run() {
TimeUnit unit = TimeUnit.SECONDS;
try {
unit.sleep(3);
System.out.println(name+" : On your mark, get set~");
unit.sleep(5);
} catch (InterruptedException e) {
System.err.println(e.getMessage());
}
System.out.println("Fire a pistol!!!");

synchronized(PrintRunner.class){
notifyAll();
}
}

当 Referee 通知运行者时,我得到一个 IllegalMonitorStateException,当我使用 wait()notifyAll() 时,我得到了 PrintRunner 锁。

请告诉我代码出错的原因。

最佳答案

您的程序无法运行,因为您在 Referee 实例上调用 notifyAll(),但它是在 block 内执行的,在 PrintRunner 上同步。类。您只能在持有锁的对象上调用 notify()/notifyAll(),否则您会得到 IllegalMonitorStateException

但是切换到 PrintRunner.class.notifyAll() 对你没有帮助,因为这个调用只会影响那些正在等待 PrintRunner.class< 上的通知的线程 对象,你没有这样的线程。您的线程正在等待特定实例,而不是类本身。因此,您的 Referee 需要遍历所有等待的 PrintRunner 实例,并对每个实例调用 notify():

for(PrintRunner runner: runners) {
synchronized(runner) {
runner.notify();
}
}

所描述的解决方案可行,但它的缺点是对所有运行者都不公平。他们中的一些人会比其他人更早收到通知。

重要说明:使用 PrintRunner.class.wait()PrintRunner.class.notifyAll() 会起作用,但会遇到同样的不公平问题,因为每个运行者都会必须重新获取单个 PrintRunner.class 实例上的锁才能取得进展。而且他们只能按顺序进行。在您的情况下(除了 wait() 调用之外,同步块(synchronized block)中没有任何内容)启动之间的延迟可以忽略不计,但它仍然会存在。


幸运的是,Java 为您的问题提供了更好的解决方案——CountDownLatch 类。特别是,您需要一个值为 1 的 CountDownLatch。所有运行者都必须等待这个闩锁,直到裁判将其设置为零。此刻,他们都将被立即释放。请注意,所有对象(裁判和运行者)必须使用相同的共享闩锁。让裁判拥有它(这是手枪):

Referee.java

private final CountDownLatch startLatch = new CountDownLatch(1);

public CountDownLatch getStartLatch() {
return startLatch;
}

@Override
public void run() {
// prepare to start, make sure all the runners are ready
// ...

// This will release all the waiting runners:
startLatch.countDown();
}

PrintRunner.java

@Override
public void run() {
try {
// This call will wait until the referee counts the latch down to zero
referee.getStartLatch().await();
} catch (InterruptedException e) {
// Handle unexpected interruption here
}
}

为了确保所有运行线程都已启动并准备就绪,您可以使用另一个初始值等于线程数的锁存器。在调用 getStartLatch().await() 之前,每个运行线程必须在它准备就绪后立即对它进行计数。在倒计时开始闩锁之前,裁判必须等待这个闩锁。这将保证您的比赛尽可能公平——所有参赛者都有时间准备,并且所有参赛者都同时被释放。

关于java - 使用同步但结果 IllegalMonitorStateException 从另一个类通知线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39188111/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com