0

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;
        }
    }
}
fabian
  • 80,457
  • 12
  • 86
  • 114
Rhidoy
  • 123
  • 1
  • 12
  • I'm sure there are questions that resemble yours more, but this is the best I've found so far: http://stackoverflow.com/questions/34510/what-is-a-race-condition – Itai Nov 30 '16 at 15:00
  • See [this answer](http://stackoverflow.com/a/19538499/4185959) (second code block). Important point: Change your flag before notifying, and more importantly - inside the `synchronized` block. – Itai Nov 30 '16 at 15:04
  • thanks for your time, in check both your link and understand the problem from first link and as your second link answer i add condition too if(!run) for wait and already my notify has same condition but still the problem exists. actually my apps do not hang completely, it work after some times if i do not exit. and i am new programmer so i do not understand your flag inside synchronized block what you actually try to means. – Rhidoy Nov 30 '16 at 15:57
  • Why do you initialize the fields that are supposed to be injected when loading the fxml? – fabian Nov 30 '16 at 16:15
  • i just started learn javafx through online resource, and few days ago i got null pointer when i do not initialize these fields that why i initialize these value. maybe that was not good practice and i learn this now from you. – Rhidoy Nov 30 '16 at 17:11

1 Answers1

1
  1. run is not volatile and is written to outside of synchronized blocks. 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:
    1. The task starts sleeping
    2. The playThread method changes run to false
    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

To fix these issues, modify the run field only from a synchronized block and use wait with a timeout instead of 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:

// 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

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();
}
fabian
  • 80,457
  • 12
  • 86
  • 114
  • i test your first solution that work but only problem is that my information updated without complete animation cycle and i know that is for use waiting(10000). so when i click fast then that waiting get notify and my loop began so my information also changed. i am testing your next two solution so that i will be able to mark it as accepted – Rhidoy Nov 30 '16 at 17:16
  • i accepted it but scheduler also give me same result as previous one, it updates my information while playing animation without waiting 10seconds. And last one work good i think i will do some work on it for learning. here problem is i have to wait 10seconds for starting the timeline animation first time and 2nd one is when i pause timeline animation, timeline animation pause at its running time and when again play it it play from that time, so i have to wait for complete its time cycle. this not big problem because has some option to set play times but problem is first 10seconds waiting. – Rhidoy Nov 30 '16 at 17:45