0

该程序的期望输出是:

乒乓乒乓乒乓

然而它在那个和之间交替

乒乓球等

问题是,我创建了 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");
    }
}
4

6 回答 6

1

因为run方法在不同的线程中运行,除非你有适当的同步,否则你不能假设哪个会先运行。如果线程先启动并不意味着它会更重要。所有的动物都是平等的。

您应该在线程中创建一个特殊标志,当线程启动时通知该标志。并在启动第二个线程之前等待标志。简单的方法是使用Condition.

于 2013-04-04T09:30:54.397 回答
1

您想强制线程运行顺序。JVM 不保证这一点,所以你必须自己动手。我可以想到两种解决方案。

丑陋的 hack,但可能有效:在启动第一个线程后但在启动第二个线程之前让出当前线程,以“鼓励”它运行。例如:

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...
}
于 2013-04-04T09:31:33.550 回答
1

JVM 不保证线程将按照它们启动的顺序启动。这就是为什么有时第二个线程首先开始的原因。

于 2013-04-04T09:31:46.533 回答
1

如果您创建两个线程,例如t1and t2,然后调用:

t1.start();
t2.start();

这并不意味着t1它将在之前开始执行t2。它可能会,但有机会t2首先开始。您必须编写自己的方法,保证t1首先开始。例如,wait()在一个对象上启动第一个线程后,以及在该notify()对象上的第一个线程开始时run()

于 2013-04-04T09:35:42.617 回答
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 等待之前通知,则测试挂起。

于 2013-04-04T09:36:39.417 回答
0

正如@evgeniy 在他的评论中已经指出的那样:标记为解决方案的答案有缺陷。printer 一个可能的解决方案是在启动第一个线程之前锁定它,因此它不能进入​​它的同步部分,直到printer.wait()被调用。

Printer printer = new Printer(6);
synchronized (printer) {
    PingPongThread pingThread = new PingPongThread(printer, "Ping");
    printer.wait();
}
PingPongThread pongThread = new PingPongThread(printer, "Pong");
于 2019-09-17T06:19:45.253 回答