该程序的期望输出是:
平 傍 平 傍 平 傍
然而,它和
之间交替出现傍 平 等
问题是,我创建了Ping线程并首先运行它。所以我不确定为什么Pong偶尔会先出现。
所以这是我的代码(易于编译)。它本质上是有效的。我只是不明白为什么它有时首先打印“Pong”。有人可以说明为什么会发生这种情况吗?
// Printer class - will be the object that both threads lock / synchronize onto
class Printer
{
int numberOfMessages;
int messageCount;
// Constructor allowing user to choose how many messages are displayed
Printer(int numberOfMessages)
{
this.numberOfMessages = numberOfMessages;
this.messageCount = 0;
}
// If more messages are to be printed, print and increment messageCount
void printMsg(String msg)
{
if (messageCount < numberOfMessages)
{
System.out.println("[" + msg + "]");
++messageCount;
}
else
{
System.exit(0);
}
}
}
// PingPong thread
class PingPongThread extends Thread
{
Printer printer;
String message;
public PingPongThread(Printer printer, String message)
{
this.printer = printer;
this.message = message;
this.start();
}
@Override
public void run()
{
while(true)
{
synchronized (printer)
{
// Print message whether it is Ping or Pong
printer.printMsg(message);
// Notify
printer.notify();
// Wait
try
{
printer.wait();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
}
// Two threads communicate with eachother to alteratively print out "Ping" and "Pong"
public class PingPong
{
public static void main(String args[])
{
Printer printer = new Printer(6);
PingPongThread pingThread = new PingPongThread(printer, "Ping");
PingPongThread pongThread = new PingPongThread(printer, "Pong");
}
}
答案 0 :(得分:1)
因为run
方法在不同的线程中运行,除非你有适当的同步,否则你不能假设哪一个会先运行。如果线程首先启动它并不意味着它会更重要。 所有动物都是平等的。
您应该在线程中创建一个特殊标志,当线程启动时会通知该标志。并在启动第二个线程之前等待标志。简单的方法是使用Condition
。
答案 1 :(得分:1)
您想强制线程运行顺序。 JVM不保证,所以你必须自己动手。我可以想到两个解决方案。
丑陋的黑客,但可能有效:在启动第一个线程之后但在启动第二个线程之前产生当前线程,以“鼓励”它运行。 E.g:
PingPongThread pingThread = new PingPongThread(printer, "Ping");
Thread.yield();
PingPongThread pongThread = new PingPongThread(printer, "Pong");
这是最简单的解决方案,但并不能保证每次都能正常工作,例如,如果另一个线程(比如事件处理程序)在收益后获取控件,那就不会有效。
更强大的方法:让主线程在启动第二个线程之前等待其他一些信号。假设此信号通过名为lock
的字段传递,这将类似于:
Object lock = new Object();
PingPongThread pingThread = new PingPongThread(lock, printer, "Ping");
lock.wait();
PingPongThread pongThread = new PingPongThread(lock, printer, "Pong");
并且线程run()
方法将类似于
synchronize (lock) { lock.notify(); }
while (true) {
// As before...
}
答案 2 :(得分:1)
JVM不保证线程将按启动顺序启动。这就是有时第二个线程首先开始的原因。
答案 3 :(得分:1)
如果你创建两个线程,例如t1
和t2
,然后调用:
t1.start();
t2.start();
这并不意味着t1
将在t2
之前开始执行。它可能,但有可能t2
首先开始。你必须编写自己的方法,保证t1
首先启动。例如,在wait()
notify()
答案 4 :(得分:1)
此
Printer printer = new Printer(6);
PingPongThread pingThread = new PingPongThread(printer, "Ping");
synchronized (printer) {
printer.wait();
}
PingPongThread pongThread = new PingPongThread(printer, "Pong");
将保证pingThread始终首先启动。
然而,线程协调仍取决于谁跑得更快。考虑一下这个
final Object obj = new Object();
Thread t1 = new Thread() {
public void run() {
synchronized (obj) {
obj.notify();
}
};
};
Thread t2 = new Thread() {
public void run() {
synchronized (obj) {
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
};
t1.start();
t2.start();
如果t1在t2等待之前通知,则测试挂起。
答案 5 :(得分:0)
正如@evgeniy在他的comment中指出的那样:标记为解决方案的答案有缺陷。一种可能的解决方案是在开始第一个线程之前锁定printer
,因此,在调用printer.wait()
之前,它无法进入其同步部分。
Printer printer = new Printer(6);
synchronized (printer) {
PingPongThread pingThread = new PingPongThread(printer, "Ping");
printer.wait();
}
PingPongThread pongThread = new PingPongThread(printer, "Pong");