简体   繁体   English

Javafx Apps在执行线程等待并通知时挂起

[英]Javafx Apps get hang when perform Thread wait and notify

i am playing some animation from my apps by using infinite loop, working well. 我正在使用无限循环从我的应用程序播放一些动画,效果很好。 i need to make wait my thread when user want and again start when user want. 我需要在用户需要时等待我的线程,并在用户需要时再次启动。 For that i used wait and notify thread by clicking my root layout, first click make my thread wait and second click make my thread run. 为此,我通过单击我的根目录布局使用了等待并通知线程,首先单击使我的线程等待,然后单击使我的线程运行。 That also work as i want. 这也像我想要的那样。

My problem is when i make click fast, it means when i make wait and also make notify instantly my Apps get hang. 我的问题是我快速点击,这意味着当我等待并立即发出通知时,我的应用程序会挂起。

So how I can fixed that problem??? 那么我如何解决这个问题呢?

below is my Code: 以下是我的代码:

public class AboutC implements Initializable {

    public VBox mainLayout;
    @FXML
    private
    Label nameLvl = new Label();
    @FXML
    private
    Label rollLvl = new Label();
    @FXML
    private
    Label batchLvl = new Label();
    @FXML
    private
    Label depLvl = new Label();
    @FXML
    private
    Label uniLvl = new Label();
    @FXML
    private Circle circle = new Circle();
    private int count = 0;
    private boolean run = true;
    private Thread thread;
    private Task task;
    private FadeTransition fd;
    private RotateTransition rt;
    private Timeline tm;

    @Override
    public void initialize(URL location, ResourceBundle resources) {

        ArrayList<AboutDevelopers> list = new ArrayList<>();
        list.add(....)

        fd = new FadeTransition(Duration.seconds(4), mainLayout);
        fd.setFromValue(0.2);
        fd.setToValue(1.0);
        fd.setCycleCount(2);

        rt = new RotateTransition(Duration.seconds(4), circle);
        rt.setByAngle(360);
        rt.setAutoReverse(true);
        rt.setCycleCount(2);

        KeyFrame keyFrame = new KeyFrame(Duration.seconds(4), new KeyValue(circle.radiusProperty(), 0));
        tm = new Timeline(keyFrame);
        tm.setCycleCount(2);
        tm.setAutoReverse(true);

        task = new Task<Void>() {
            @Override
            synchronized public Void call() throws Exception {
                int i = 0;
                while (true) {
                    if (run) {
                        Platform.runLater(() -> {
                            nameLvl.setText(list.get(count).getName());
                            rollLvl.setText("Roll: " + list.get(count).getRoll());
                            batchLvl.setText("Batch: " + list.get(count).getBatch());
                            depLvl.setText("Department: " + list.get(count).getDepartment());
                            uniLvl.setText(list.get(count).getUniversity());
                            circle.setFill(new ImagePattern(new Image(list.get(count).getImagePath())));

                            fd.play();
                            rt.play();
                            tm.play();

                            count++;
                            if (count >= list.size())
                                count = 0;
                        });
                        sleep(10000);
                    } else
                        wait();
                }
            }
        };
        thread = new Thread(task);
        thread.setDaemon(true);
        thread.start();
    }

    void setStage(Stage stage) {
        stage.setOnCloseRequest(event -> {
            thread.interrupt();
        });
    }

    public void playThread(){
        if (run) {
            run = false;
        } else {
            if(!run){
                synchronized (task) {
                    task.notify();
                }
            }
            run = true;
        }
    }
}
  1. run is not volatile and is written to outside of synchronized blocks. run不是volatile并且被写入同步块的外部。 This means the task may never see the updated value. 这意味着任务可能永远不会看到更新后的值。
  2. Using Thread.sleep(10000) you do not release the lock on the Task which means the following could happen: 使用Thread.sleep(10000)不会释放对Task的锁定,这意味着可能发生以下情况:
    1. The task starts sleeping 任务开始休眠
    2. The playThread method changes run to false playThread方法更改runfalse
    3. The playThread method is invoked again and tries to aquire a lock on the task object which the task still keeps itself leading to the calling thread to be blocked for up to 10 sec 再次调用playThread方法,并尝试在任务对象上获取一个锁,该任务仍然保持自身状态,导致调用线程被阻塞长达10秒

To fix these issues, modify the run field only from a synchronized block and use wait with a timeout instead of sleep : 要解决这些问题,请仅从同步块修改run字段,并使用带有超时的wait而不是sleep

while (true) {
    if (run) {
        Platform.runLater(() -> {
            nameLvl.setText(list.get(count).getName());
            rollLvl.setText("Roll: " + list.get(count).getRoll());
            batchLvl.setText("Batch: " + list.get(count).getBatch());
            depLvl.setText("Department: " + list.get(count).getDepartment());
            uniLvl.setText(list.get(count).getUniversity());
            circle.setFill(new ImagePattern(new Image(list.get(count).getImagePath())));

            fd.play();
            rt.play();
            tm.play();

            count++;
            if (count >= list.size())
                count = 0;
        });
        wait(10000);
    } else
        wait();
}
public void playThread(){
    synchronized (task) {
        run = !run;
        if (run) {
            task.notify();
        }
    }
}

This means however starting and stoping the task may speed up the update frequency... 这意味着,开始和停止任务可能会加快更新频率。


Alternative: 选择:

Use a ScheduledExecutorService to schedule updates regularly: 使用ScheduledExecutorService定期安排更新:

// TODO: shut this down after you're done with it???
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(r -> {
    Thread t = new Thread(r);
    t.setDaemon(true);
    return t;
});

@Override
public void initialize(URL location, ResourceBundle resources) {
    ...
    startTask();
}

private final Runnable updateRunnable = () -> {
    Platform.runLater(() -> {
        nameLvl.setText(list.get(count).getName());
        rollLvl.setText("Roll: " + list.get(count).getRoll());
        batchLvl.setText("Batch: " + list.get(count).getBatch());
        depLvl.setText("Department: " + list.get(count).getDepartment());
        uniLvl.setText(list.get(count).getUniversity());
        circle.setFill(new ImagePattern(new Image(list.get(count).getImagePath())));

        fd.play();
        rt.play();
        tm.play();

        count++;
        if (count >= list.size())
            count = 0;
        }
    });
};

private ScheduledFuture scheduledFuture;

private void startTask() {
    scheduledFuture = executor.scheduleWithFixedDelay(updateRunnable, 0, 10000, TimeUnit.MILLISECONDS);
}

public void playThread() {
    if (scheduledFuture == null) {
        // nothing running currently
        startTask();
    } else {
        scheduledFuture.cancel();
        scheduledFuture = null;
    }
}

Or in a way more suitable to JavaFX 或者以更适合JavaFX的方式

Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(10), evt -> {

        nameLvl.setText(list.get(count).getName());
        rollLvl.setText("Roll: " + list.get(count).getRoll());
        batchLvl.setText("Batch: " + list.get(count).getBatch());
        depLvl.setText("Department: " + list.get(count).getDepartment());
        uniLvl.setText(list.get(count).getUniversity());
        circle.setFill(new ImagePattern(new Image(list.get(count).getImagePath())));

        fd.play();
        rt.play();
        tm.play();

        count++;
        if (count >= list.size())
            count = 0;
        }
    });

}));
timeline.setCycleCount(Animation.INDEFINITE);

timeline.play();
if (timeline.getStatus == Animation.Status.RUNNING) {
    timeline.stop();
} else {
    timeline.play();
}

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

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