这是某种Java拼图游戏,我偶然发现,无法真正解释。也许有人可以吗?

短时间后,以下程序挂起。有时在2个输出之后,有时在80个输出之后,但几乎总是在正确终止之前。如果第一次没有发生,您可能必须运行几次。

public class Main {
    public static void main(String[] args) {
        final WorkerThread[] threads = new WorkerThread[]{ new WorkerThread("Ping!"), new WorkerThread("Pong!") };
        threads[0].start();
        threads[1].start();

        Runnable work = new Runnable() {
            private int counter = 0;
            public void run() {
                System.out.println(counter + " : " + Thread.currentThread().getName());
                threads[counter++ % 2].setWork(this);
                if (counter == 100) {
                    System.exit(1);
                }
            }
        };

        work.run();
    }
}

class WorkerThread extends Thread {
    private Runnable workToDo;

    public WorkerThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        while (true){
            if (workToDo != null) {
                workToDo.run();
                workToDo = null;
            }
        }
    }

    public void setWork(Runnable newWork) {
        this.workToDo = newWork;
    }
}


现在,很明显,繁忙的等待循环通常不是一个好主意。但这不是改善,而是了解正在发生的事情。

WorkerThread.setWork()synchronizedWorkerThread.workToDo字段设置为volatile时,由于一切正常,因此我怀疑存在内存问题。

但是为什么会这样呢?调试无济于事,一旦您开始逐步执​​行,一切都会按预期进行。

一个解释将不胜感激。

最佳答案

这些行之间直接发生问题:

workToDo.run();
workToDo = null;


假设发生以下事件序列:

- Original Runnable runs.  "Ping!".setWork() called
- Ping! thread realizes workToDo != null, calls run(), the stops between those two lines
  - "Pong!".setWork() called
- Pong! thread realizes workToDo != null, calls run()
  - "Ping!".setWork() called
- Ping! thread resumes, sets workToDo = null, ignorantly discarding the new value
- Both threads now have workToDo = null, and the counter is frozen at 2,...,80
  Program hangs

07-24 12:47