Luiz Roseiro
Luiz Roseiro

Reputation: 63

How to run some code repeatedly after an interval that can changes?

I'm using a thread in JavaFX to repeat my code after an interval (initially 1s), but I want to be able change the interval that the thread is using to 500ms or 333ms based on user choice (I have a button in a menu bar to change for each choice). I did tried things like shutDown() if the user clicks on one of the buttons and initiate it again with the new value, but didn't work. Any ideas?

Here's the relevant part of my code:

ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(() -> {
        //refresh users, line and "guiche"
        updateFila(usuarios, guiches, fila);
        updateGuiche(guiches, fila, graphicsContext);
        turno++;
        //ends the code after the end of the line
        if (done) {
            exec.shutdown();
        }
}, 0, 1000, TimeUnit.MILLISECONDS); //This is interval that I need to change after user choice


I know that I'm executing scheduleAtFixedRate() right now, but it was just to see if the logic was fine.

Additionally, I need to pause, resume and reset the thread, all based on user click.

Upvotes: 1

Views: 106

Answers (2)

fabian
fabian

Reputation: 82461

You could use a Timeline to execute a event handler every second and set the rate at which the animation runs to the number of times the update should happen per second, i.e. 2 or 3...

In the following example I use 5 instead of 3 for a more recognizable effect:

@Override
public void start(Stage primaryStage) {
    Line line = new Line(25, 125, 125, 125);
    Rotate rotate = new Rotate(0, 125, 125);
    line.getTransforms().add(rotate);

    ToggleButton btn = new ToggleButton();
    btn.textProperty().bind(Bindings.when(btn.selectedProperty()).then("5 Hz").otherwise("2 Hz"));
    StackPane.setAlignment(btn, Pos.BOTTOM_LEFT);

    // rotate by one 60th of a full rotation each time
    Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), evt -> rotate.setAngle((rotate.getAngle() + (360d / 60d)) % 360)));
    timeline.setCycleCount(Animation.INDEFINITE);

    // rate depends on button state
    timeline.rateProperty().bind(Bindings.when(btn.selectedProperty()).then(5d).otherwise(2d));

    Pane linePane = new Pane(line);
    linePane.setMinSize(250, 250);
    linePane.setMaxSize(250, 250);
    StackPane root = new StackPane();
    root.getChildren().addAll(linePane, btn);

    Scene scene = new Scene(root, 300, 300);

    primaryStage.setScene(scene);
    timeline.play();

    primaryStage.show();
}

The binding is simply an example of setting the update frequency. You could of course use different means to assign this value, e.g.

ComboBox<Duration> combo = new ComboBox<>();
Duration initial = Duration.seconds(1);
combo.getItems().addAll(initial, Duration.seconds(1/3d), Duration.seconds(1/2d));
combo.setValue(initial);
combo.valueProperty().addListener((observable, oldValue, newValue) -> timeline.setRate(1/newValue.toSeconds()));

Upvotes: 1

rvit34
rvit34

Reputation: 2116

If you use only single thread you can create your own implementation based on classic thread.

public class Worker extends Thread {

private static final Logger logger = Logger.getLogger(Worker.class);

private volatile int delayInSec = 1;

private CountDownLatch latch;

private final int STARTED = 0;
private final int STOPPED = 1;
private volatile int state = STOPPED;

public Worker(){}


@Override
public void run() {

    logger.debug("enter to execution method");

    while (!isInterrupted()) {

        // stop if needed (it's additional feature)
        if (state == STOPPED) {
            logger.debug("stopped and locked");
            try {
                latch = new CountDownLatch(1);
                latch.await();
            } catch (InterruptedException e) {
                logger.warning("got interruption while waiting for action ", e);
                break;
            }
            logger.debug("awake");
        }

        // do your stuff here

        try {
            // then delay
            logger.debug("go to sleep for %s sec.",delayInSec);
            latch = new CountDownLatch(1);
            latch.await(delayInSec, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            logger.warning("got interruption while waiting for action ", e);
            break;
        }


    }

    logger.debug("exit from execution method");

}


public void startJob(){
    state = STARTED;
    logger.debug("started");
    if (latch!=null)
       latch.countDown();
}

public void stopJob(){  
    state =  STOPPED;
    logger.debug("stopped");
}


public void shutdown(){
    logger.debug("shutdown");
    interrupt();
}

public void changeDelay(int delayInSec) {
    logger.debug("set new delay %s", delayInSec);
    this.delayInSec = delayInSec;       
}


}

Upvotes: 0

Related Questions