Java JavaFX应用程序中的多线程问题

时间:2016-01-20 01:11:09

标签: java multithreading javafx

我正在编写一个实时做大量工作的程序,它从视频处理图像并在JavaFx ImageView上显示图像,问题是我无法更新主线程组件从另一个线程来看,java不是线程安全的,所以我使用Timer的方式代替,一个线程一直滞后,它主要是挂起,所以下面是我如何实现我的代码

TimerTask frame_grabber = new TimerTask()
    {
        @Override
        public void run() 
        {

            processVideo();
              Platform.runLater(new Runnable() {
                @Override public void run() {
                    imageView.setImage(tmp);
                }
            });
        }
    };
    timer = new Timer();

    Double period = 1000 / getFPS() * 2;            
    this.timer.schedule(frame_grabber, 0, period.longValue());

这似乎工作得更好,但我的整个GUI都很懒散,有人可以建议我更好的方式来处理视频和更新我的UI而不会造成任何滞后吗?

1 个答案:

答案 0 :(得分:1)

如果我正确理解了您的问题,您基本上希望通过ImageView显示视频,确保您不会通过快速发送图像来淹没FX应用程序线程,而不是显示它们。是吗?

如果是这样,您可以执行以下操作:

AtomicReference<Image> latestImage = new AtomicReference<>();

TimerTask frameGrabber = new TimerTask() {
    @Override
    public void run() {
        if (latestImage.setAndGet(processVideo()) == null) {
            Platform.runLater(() -> imageView.setImage(latestImage.setAndGet(null)));
        }
    }
};

// rate at which you want to sample video:
double sampleRate = ... ;
long sampleMillis = (long) 1000 / sampleRate ;
this.timer.schedule(frameGrabber, 0, sampleMillis);

在此代码中,您确保不要使用太多请求来泛滥FX应用程序线程。计时器任务将latestImage设置为它抓取的最新图像。 Platform.runLater() runnable获取图片并将latestImage设置为null,表示已为新图片做好准备。如果最后一个已经被消耗,则计时器仅将新的runnable调度到Platform.runLater()。结果是FX应用程序线程将消耗尽可能多的图像,但如果图像的生成速度比它可以消耗的速度快,那么将跳过中间图像。

使用AtomicReference可确保检索值并在latestImage中设置新值以原子方式进行管理,确保没有竞争条件。

我假设您在这里修改了processVideo()方法,以便它返回它抓取的图像:

private Image processVideo() {
    Image image = ... // grab image
    return image ;
}