while 循环的奇怪行为

Strange behavior from a while loop

所以我在写一个视频播放库,细节不重要。发生的事情是这段代码需要 运行 在解码器线程的 运行() 方法的末尾:

System.out.println("Video decoding complete");
int a = 0, b = 0;
do
{
    a = pictures.getCount();
    b = samples.getCount();
}while(a > 0 || b > 0);
Gdx.app.log("Status", videoPath + " completed playing successfully.");
videoComplete = true;

问题是,任何通过 do{}while 的东西都不会执行。这是奇怪的部分,这段代码在 while 循环中添加 System.out.println 时执行:

System.out.println("Video decoding complete");
int a = 0, b = 0;
do
{
    System.out.println("Waiting for packets to drain.");
    a = pictures.getCount();
    b = samples.getCount();
}while(a > 0 || b > 0);
Gdx.app.log("Status", videoPath + " completed playing successfully.");
videoComplete = true;

我怀疑编译器知道我正试图将它变成 运行 一个什么都不做的循环,它只是剪掉代码或其他东西。但我真的不知道发生了什么。如果有人比我更了解,我希望有更好的解决方案。这么简单的事情我就挂在这里了!

我的猜测是 pictures.getCount() 和 samples.getCount() 读取非 volatile 字段。当您只读取一个非易失性字段时,出于性能原因,它可以被内联,但是如果您执行类似调用 synchronized 方法的操作(并且 System.out 是同步的),它不会以这种方式优化代码并且每次都必须执行查找。

我建议你尝试添加一个空的同步块,看看它是否仍然有效,即这样做而不是 println

synchronized(this) { }

我最终按照@slim 的建议这样做了:

                System.out.println("Video decoding complete");
                this.decoderComplete = true;
                //wait until notified that packets are done draining
                synchronized(this)
                {
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        videoComplete = true;
                        this.container.close();
                        e.printStackTrace();
                    }
                }
                Gdx.app.log("Status", videoPath + " completed playing successfully.");
                videoComplete = true;
                this.container.close();

在另一个线程中,一旦我们知道我们已经完成了所有数据包的读取:

if(this.packetHandlerRunnable.getNumAudioPackets() <= 0 
                && this.packetHandlerRunnable.getNumVideoPackets() <= 0 
                && this.packetHandlerRunnable.isDecoderComplete())
        {
            synchronized(packetHandlerRunnable)
            {
                this.packetHandlerRunnable.notify();
            }
        }