【问题标题】:JavaFX: how to read CurrentTime of MediaPlayer while not in JavaFX App Thread [duplicate]JavaFX:不在JavaFX App Thread中时如何读取MediaPlayer的CurrentTime [重复]
【发布时间】:2020-01-11 05:36:31
【问题描述】:

是否有线程安全的方式来读取 JavaFX MediaPlayer 的属性(例如 CurrentTime)?

要播放/更改媒体,我通常使用Platform.runLater,但我需要立即返回CurrentTime

myplayer.getCurrentTime() 安全吗?或者如果player 被另一个线程处理掉,我会遇到麻烦吗?

示例

private MediaPlayer player;

public void playMe(){
    Platform.runLater(
        new Runnable() {
        @Override
        public void run() {
            player = new MediaPlayer(media);
            player.play();
    }});
}
public void deleteMe(){
    Platform.runLater(
        new Runnable() {
        @Override
        public void run() {
            if (player != null) player.dispose();
            player = null;
    }});
}

public Double getCurrentTime(){
    if (player != null) return player.getCurrentTime().toSeconds(); // thread issues???
    else return null;
}

【问题讨论】:

  • 您的代码不完整。请阅读上面的链接。
  • 您的链接不相关 - 我不想重现问题。
  • 您的代码应该可以被复制。它还应该使用最少的代码重现您当前遇到的问题。
  • 确保您确实需要这样做。在 JavaFX 应用程序线程上执行所有操作,除非您有充分的理由不这样做。这样您就不必担心并发问题,您可能会发现这些问题很难处理。

标签: javafx


【解决方案1】:

JavaFX 属性不是线程安全的。不保证内存一致性:Java 可以随时为后台线程创建包含属性对象的内存副本。发生这种情况时,后台线程将看不到稍后发生的任何属性更改。

不过,确保可以从线程访问该值并不难。根据访问频率和您愿意接受的信息检索延迟,以下方法可能适合您:

从监听器更新AtomicReference

这样,您只需将值分配给应用程序线程上的AtomicReference,即可确保更新对后台线程可见:

final AtomicReference<Duration> time = new AtomicReference<>(player.getCurrentTime());
player.currentTimeProperty().addListener((o, oldValue, newValue) -> time.set(newValue));

Thread t = new Thread(() -> {
    while (true) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
        }
        System.out.println(time.get());
    }
});
t.setDaemon(true);
t.start();

缺点是对引用的更新比必要的更频繁。 (顺便说一句,volatile 字段也可以解决问题。)

使用Platform.runLater查询属性

作为替代方案,您可以使用Platform.runLater 简单地安排一个可运行的读取变量。这种方法不需要引用不断更新:

Thread t = new Thread(() -> {
    while (true) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
        }
        CompletableFuture<Duration> future = new CompletableFuture<>();
        
        Platform.runLater(() -> future.complete(player.getCurrentTime()));
        try {
            Duration time = future.get(); // get value as soon as evaluated on application thread
            System.out.println(time);
        } catch (InterruptedException e) {
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
    }
});
t.setDaemon(true);
t.start();

注意:对于这两种方法,您需要处理可以将player 字段设置为null 的事实。请注意,任何测试都会遇到与答案开头所述相同的内存一致性问题。第一种方法需要您创建字段volatile 以确保更改对后台线程也是可见的,第二种方法您可以取消未来或引发异常以通知调用者:future.completeExceptionallyfuture.cancel(true) 导致 future.get() 分别产生 ExecutionExceptionCancelationException

【讨论】:

    猜你喜欢
    • 2015-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多