我的notifyAll()方法似乎都没有工作。 露西假设要等到鲍勃到达然后释放。 Bob假设等待Lucy的确认,然后释放。 这些事情似乎都没有发生。
有人能让我知道我做错了什么以及如何解决这个问题。 提前谢谢。
编辑 - 我使用Grays建议修改了我的代码。 异常消失,但notify()方法似乎仍然无效。
import java.util.logging.Level;
import java.util.logging.Logger;
public class PlayDates {
Thread lucyThread;
Girl lucy;
Thread bobThread;
Boy bob;
public static void main(String[] args) {
PlayDates playDates = new PlayDates();
playDates.run();
}
public void run() {
lucy = new Girl();
lucyThread = new Thread(lucy);
bob = new Boy();
bobThread = new Thread(bob);
lucyThread.start();
threadSleep(500);
bobThread.start();
}
public class Girl implements Runnable {
@Override
public void run() {
synchronized(PlayDates.this){
System.out.println("Girl synchronized hit");
if(!bob.hasArrived()) { // Doesnt seem to get past here?
System.out.println("Lucy has fallen asleep waiting for Bob");
try {
PlayDates.this.wait(); // Wait for Bob
System.out.println("Lucy has woken up");
PlayDates.this.notifyAll(); // Acknowledge Bobs arrival
} catch (InterruptedException ex) {
Logger.getLogger(PlayDates.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
}
public class Boy implements Runnable {
private boolean hasArrived;
@Override
public void run() {
synchronized(PlayDates.this){
System.out.println("Bob has arrived to play");
PlayDates.this.notifyAll();
try {
PlayDates.this.wait(); // Wait for Lucy to acknowledge Bobs arrival
} catch (InterruptedException ex) {
Logger.getLogger(PlayDates.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("Bob and Lucy are playing");
}
}
public Boy() {
hasArrived = true;
}
public boolean hasArrived() {
return hasArrived;
}
}
public void threadSleep(int milli) {
try {
Thread.sleep(milli);
} catch (InterruptedException ex) {
Logger.getLogger(PlayDates.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
当前输出
Girl synchronized hit
Bob has arrived to play
编辑2 我改编了我的代码,正如格雷斯的建议所暗示的那样。 hasArrived现在是volatile并且在playDates中运行方法。 它在内部类Boys run方法中变为true。 输出没有改变,问题看起来是一样的。 还有什么进一步的建议吗?
更新的代码:
import java.util.logging.Level;
import java.util.logging.Logger;
public class PlayDates {
Thread lucyThread;
Girl lucy;
Thread bobThread;
Boy bob;
volatile boolean hasArrived;
public static void main(String[] args) {
PlayDates playDates = new PlayDates();
playDates.run();
}
public void run() {
hasArrived = false;
lucy = new Girl();
lucyThread = new Thread(lucy);
bob = new Boy();
bobThread = new Thread(bob);
lucyThread.start();
threadSleep(500);
bobThread.start();
}
public class Girl implements Runnable {
@Override
public void run() {
synchronized(PlayDates.this){
System.out.println("Girl synchronized hit");
if(hasArrived) { // Doesnt seem to get past here?
System.out.println("Lucy has fallen asleep waiting for Bob");
try {
PlayDates.this.wait(); // Wait for Bob
System.out.println("Lucy has woken up");
PlayDates.this.notifyAll(); // Acknowledge Bobs arrival
} catch (InterruptedException ex) {
Logger.getLogger(PlayDates.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
}
public class Boy implements Runnable {
@Override
public void run() {
threadSleep(1000);
synchronized(PlayDates.this){
System.out.println("Bob has arrived to play");
hasArrived = true;
PlayDates.this.notifyAll();
try {
PlayDates.this.wait(); // Wait for Lucy to acknowledge Bobs arrival
} catch (InterruptedException ex) {
Logger.getLogger(PlayDates.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("Bob and Lucy are playing");
}
}
}
public void threadSleep(int milli) {
try {
Thread.sleep(milli);
} catch (InterruptedException ex) {
Logger.getLogger(PlayDates.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
答案 0 :(得分:3)
在初始化Girl
字段之前,您正在启动bob
主题,以便获得NPE。您应该在 bob
之前初始化Girl
字段,并将其传递到该主题。您的程序可能在某些情况下有效,但存在无法预测的竞争条件。如果线程启动得足够快,它可能会起作用,但您应该在启动bob
线程之前初始化Girl
。
您还有一些memory synchronization issues。例如,虽然您正在PlayDates.this
进行同步,但Boy
线程调用Girl
时,bob.hasArrived()
类可能尚未初始化和同步。每当在多个线程中访问字段时,您需要确保两个线程都看到正确同步的值。您可以将hasArrived
设为AtomicBoolean
或将hasArrived
标记为volatile
来确保这一点。
修改强>
问题在于改变,所以我会努力跟上。我建议 not 将hasArrived
设置为Boy
中的volatile
。我认为您应该将其设为run()
并在Girl
方法中设置。您希望Boy
线程启动,运行一点,然后看到wait()
不可用且Boy
。因此,hasArrived
线程应该稍后开始,并在true
后的run()
方法中将sleep()
设置为{{1}}。