2016-01-20 57 views
0

我正在編寫一個程序,它將實時執行相當多的工作,它處理來自視頻的圖像並在JavaFx上顯示圖像ImageView,問題是我無法更新從另一個線程主線程的組件,所以我雖然使用一個Timer代替的一種方式,一個線程一直落後Java是不是線程安全的,它主要是掛起,所以下面是如何我已經實現了我的代碼Java中的多線程問題JavaFX應用程序

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是laggying,有人可以建議我一個更好的方式處理視頻和更新我的UI沒有造成任何滯後?

+0

你可以嘗試去耦從UI幀速率定時器的預設速率。 JavaFX幀速率約爲60 fps,你可能想更頻繁地調用'TimerTask'。更確切地說,在任何建議中,你可能應該發佈'processVideo'的代碼。 – hotzst

回答

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()獲取圖像並將latestImage設置爲null,表明它已準備好創建新圖像。如果最後一個消耗完了,定時器只會安排一個新的可運行到Platform.runLater()。結果是FX應用程序線程會盡可能多地消耗圖像,但如果圖像產生的速度比消耗的速度快,那麼中間圖像將被跳過。

使用AtomicReference可確保以原子方式管理latestImage中的檢索值和設置新值,確保沒有競爭條件。

我假設的修改,您processVideo()方法在這裏,所以它返回的圖像它抓住:

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

美麗的答案,你救了我的命。 –