JAVA ScheduledExecutorService only runs once when

2019-08-08 01:26发布

问题:

When I had my logic inside a Runnable it worked fine except I could not interact with the UI Thread. So I am trying to put everything inside a class that extends Task and it works Except the task is only executed once. No errors and I get a succeeded message form the Task succeeded method.

I have also tried making the task return Boolean true in the call method but that did not help.

public class Main extends Application { 
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception{   
        SyncTask syncTask = new SyncTask();

        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
        executor.scheduleAtFixedRate(syncTask, 0, 10, TimeUnit.SECONDS);

        Label syncEngineLabel = centralController.getScheduleTabMessageLabel();
        syncEngineLabel.textProperty().bind(syncTask.messageProperty());
    }
    class SyncTask extends Task<Void>{
        private Schedule schedule = null;

        public SyncTask() {}

        @Override
        protected Void call() throws Exception {
            System.out.println("we are in the task...");
            if (getScheduleFromApi()){
                updateMessage("New Schedule Retrieved...");
            }
            return null;
        }
        @Override protected void succeeded() {
            super.succeeded();
            System.out.println("succeeded");
        }

        @Override protected void cancelled() {
            super.cancelled();
            System.out.println("cancelled");
        }

        @Override protected void failed() {
            super.failed();
            System.out.println("failed");
        }

        private Boolean getScheduleFromApi(){
            Gson gson = new GsonBuilder().serializeNulls().create();
            ApiGet api = new ApiGet("schedule/get-schedule-by-room", parameters);
            api.sendRequest();

            if (api.isSuccess()){
                schedule = gson.fromJson(api.response(), Schedule.class);
                if (schedule.getStatus().equals("200")){
                    return true;
                }
                else{
                    updateMessage(schedule.getMsg());
                    return false;
                }
            }
            else {
                updateMessage("Failed to process API call to ASI Server.");
                return false;
            }
        }
    }
}

Please note that this code actually exists inside a controller but I put it in Main here to try and provide self contained code.

Thanks!

回答1:

The ScheduledExecutorService will simply treat the task you provide as a Runnable, and try to reuse the same task instance every time it runs, which is explicitly forbidden in the documentation.

Use a ScheduledService instead:

@Override
public void start(Stage primaryStage) throws Exception{   

    ScheduledService<Void> scheduledService = new ScheduledService<Void>() {
        @Override
        protected Task<Void> createTask() {
            return new SyncTask();
       }
    };
    scheduledService.setPeriod(Duration.seconds(10));
    scheduledService.start();

    Label syncEngineLabel = centralController.getScheduleTabMessageLabel();
    scheduledService.stateProperty().addListener((obs, oldState, newState) -> {
            if (newState == Worker.State.RUNNING) {
                syncEngineLabel.setText("Sync in progress");
            } else if (newState == Worker.State.FAILED) {
                syncEngineLabel.setText("Sync error");
            } else {
                syncEngineLabel.setText("Sync complete");
            }
    });


}