简体   繁体   English

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

[英]Multi threading issues in Java JavaFX application

I am writing a program that is to do quite a lot of work in real time, its to process Images from a video and display the Images on a JavaFx ImageView , the issue is I cannot update main thread components from another thread, java is not thread safe so I though of a way of using a Timer instead, a thread keeps lagging behind, it mostly hangs, so below is how I have implemented my code 我正在编写一个实时执行大量工作的程序,它可以处理视频中的图像并在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());

This seems to work better but the my entire GUI is laggying, can someone suggest me a better way of processing the video and updating my UI without causing any lags? 这似乎工作得更好,但是我的整个GUI都滞后,有人可以建议我一种更好的方式处理视频并更新UI而不引起任何滞后吗?

If I understand your question correctly, you basically want to display a video via an ImageView , making sure that you don't flood the FX Application Thread by sending it images faster than it can display them. 如果我正确理解了您的问题,那么您基本上是想通过ImageView显示视频,请确保不会通过发送比其显示图像快的图像来淹没FX Application Thread。 Is that right? 那正确吗?

If so, you can do something like: 如果是这样,您可以执行以下操作:

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);

In this code, you ensure you don't flood the FX Application thread with too many requests. 在此代码中,请确保不会向FX Application线程发送过多请求。 The timer task sets latestImage to the latest image it has grabbed. 计时器任务将latestImage设置为它已latestImage的最新图像。 The Platform.runLater() runnable gets an image and sets latestImage to null , indicating it is ready for a new image. 可运行的Platform.runLater()获取图像,并将latestImage设置为null ,表明它已准备好用于新图像。 The timer only schedules a new runnable to Platform.runLater() if the last one has been consumed. 如果最后一个消耗完了,计时器只会为Platform.runLater()安排一个新的可运行对象。 The result is that the FX Application Thread will consume as many images as it can, but if images are produced more quickly than it can consume then, intermediate images will be skipped. 结果是FX Application Thread将消耗尽可能多的图像,但是如果生成的图像比其消耗的速度快,则将跳过中间图像。

Using the AtomicReference ensures that retrieving a value and setting a new value in latestImage are managed atomically, making sure there are no race conditions. 使用AtomicReference确保检索值和设定一个新的值latestImage原子地管理,确保不存在竞争条件。

I'm assuming a modification to your processVideo() method here, so that it returns the image it grabs: 我在这里假设对您的processVideo()方法进行了修改,以便它返回其获取的图像:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM