执行线程等待和通知时,Javafx应用程序会挂起

时间:2016-11-30 14:47:09

标签: java multithreading javafx

我正在使用无限循环从我的应用程序播放一些动画,效果很好。我需要在用户需要时等待我的线程,并在用户需要时再次启动。为此,我通过单击我的根布局使用等待和通知线程,首先单击使我的线程等待,然后第二次单击使我的线程运行。这也像我想要的那样工作。

我的问题是,当我快速点击时,这意味着当我等待并立即通知我的应用程序会挂起。

那我怎么能解决这个问题?

下面是我的代码:

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 个答案:

答案 0 :(得分:1)

  1. run不是volatile,而是写在synchronized块之外。这意味着任务可能永远不会看到更新的值。
  2. 使用Thread.sleep(10000)您不会释放Task上的锁定,这意味着可能发生以下情况:
    1. 任务开始睡觉
    2. playThread方法将run更改为false
    3. 再次调用playThread方法,并尝试获取任务对象上的锁定,该任务仍然保持自己导致调用线程被阻止最多10秒
  3. 要修复这些问题,请仅修改同步块中的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();
            }
        }
    }
    

    这意味着启动和停止任务可能会加快更新频率......

    替代:

    使用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;
        }
    }
    

    或者更适合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();
    }