Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8123273

GUI easily locked up by excessive GUI updates

    XMLWordPrintable

Details

    Description

      Our application may be in a state where it is busily processing data from multiple threads and updating the UI (via Platform.runLater and careful not to flood the queue with unbounded requests)
      This prevents any input events from being processed. Typing and clicking have no effect.

      This appears to be less of a problem on JavaFX 8. But this is a major problem on JavaFX 2.2.x.

      Sample code follows:
      package toobusy;

      import static java.lang.Thread.sleep;
      import java.util.ArrayList;
      import java.util.List;
      import java.util.concurrent.atomic.AtomicBoolean;
      import java.util.logging.Level;
      import java.util.logging.Logger;
      import javafx.application.Application;
      import javafx.application.Platform;
      import javafx.beans.binding.Bindings;
      import javafx.beans.property.SimpleIntegerProperty;
      import javafx.beans.value.ChangeListener;
      import javafx.beans.value.ObservableValue;
      import javafx.event.ActionEvent;
      import javafx.event.EventHandler;
      import javafx.scene.Scene;
      import javafx.scene.control.CheckBox;
      import javafx.scene.control.Label;
      import javafx.scene.control.ToggleButton;
      import javafx.scene.layout.VBox;
      import javafx.scene.text.Text;
      import javafx.stage.Stage;

      public class TooBusy extends Application {

      List<Thread> workers = new ArrayList<>();
      ToggleButton btn;
      Text someText;
      private volatile boolean doTheSleep;
      SimpleIntegerProperty theCount = new SimpleIntegerProperty();
      PlatformRunner workUnit = new PlatformRunner();

      @Override
      public void start(Stage primaryStage) {
      someText = new Text("Just some text");
      final CheckBox delay = new CheckBox("sleep(1) <-- uncheck and no UI input is processed");
      doTheSleep = true;
      delay.setSelected(doTheSleep);
      delay.setOnAction(new EventHandler<ActionEvent>() {
      @Override
      public void handle(ActionEvent t) {
      doTheSleep = delay.isSelected();
      }
      });
      btn = new ToggleButton("Active");
      btn.selectedProperty().addListener(new ChangeListener<Boolean>() {
      @Override
      public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
      if (t1) {
      startWorker();
      } else {
      stopWorker();
      }
      }
      });

      VBox root = new VBox(8);
      root.getChildren().addAll(btn, delay);
      for (int i = 0; i < 4; i++) {
      Label countLabel = new Label();
      countLabel.textProperty().bind(Bindings.format("The count is %d", theCount));
      root.getChildren().add(countLabel);
      }
      root.getChildren().add(someText);

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

      primaryStage.setTitle("Rendering stops events?");
      primaryStage.setScene(scene);
      primaryStage.show();
      }

      private void startWorker() {
      for (int i = 0; i < 4; i++) {
      Thread worker = createWorker();
      workers.add(worker);
      worker.start();
      }
      System.out.println("Started");
      }

      private void stopWorker() {
      System.out.println("Stopping...");
      for (Thread t : workers) {
      t.interrupt();
      }
      for (Thread t : workers) {
      try {
      t.join();
      } catch (InterruptedException ex) {
      Logger.getLogger(TooBusy.class.getName()).log(Level.SEVERE, null, ex);
      }
      }
      workers.clear();
      System.out.println("Stopped.");
      }
      static int tc;

      private Thread createWorker() {
      return new Thread("My Busy Worker " + (tc++)) {
      PlatformRunner workUnit = new PlatformRunner();

      @Override
      public void run() {
      try {
      while (!interrupted()) {
      workUnit.runLater();
      if (doTheSleep) {
      sleep(1);
      }
      }
      } catch (InterruptedException ex) {
      // intentionally ignored
      }
      }
      };
      }

      public class PlatformRunner implements Runnable {

      AtomicBoolean pending = new AtomicBoolean(false);

      public void runLater() {
      // Only allow one instance of this to be on the queue at a time
      // so they can't queue up indefinitely
      if (pending.compareAndSet(false, true)) {
      Platform.runLater(this);
      }
      }

      @Override
      public final void run() {
      pending.set(false);
      theCount.set(theCount.get() + 1);
      someText.rotateProperty().bind(theCount);
      }
      };

      static void main(String[] args) {
      launch(args);
      }
      }

      Attachments

        Issue Links

          Activity

            People

              anthony Anthony Petrov (Inactive)
              swpalmer Scott Palmer
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:
                Imported: