来自while循环的奇怪行为

时间:2015-04-19 22:07:23

标签: java multithreading compiler-optimization

所以我写了一个视频播放库,细节并不重要。发生了什么是这段代码需要在解码器线程的run()方法结束时运行:

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循环中添加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;

我怀疑编译器知道我试图让它运行一个循环什么都不做,它只是剪掉代码或其他东西。但实际上我不知道发生了什么。如果有人比我更了解,我很乐意找到更好的解决方案。我在这里挂了这么简单的事情!

2 个答案:

答案 0 :(得分:4)

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

我建议您尝试添加一个空的同步块,看看这是否仍然有效,即执行此操作而不是println

synchronized(this) { }

答案 1 :(得分:0)

根据@ 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();
            }
        }