4 Replies Latest reply: May 13, 2012 2:04 PM by 935477 RSS

    Is this program thread safe?

    935477
      This program does not throw any exception or error.

      But sec Property is binding with secRotate's angleProperty and secRotate angleProperty is added to secHand's Transforms. This means that secProperty eventually affects Graphics.

      Do I have to call Platform.runLater() ?

      h2. Clock
      public class Clock extends Application {
           private Calendar calendar = Calendar.getInstance();
           private IntegerProperty sec = new SimpleIntegerProperty();
      
           @Override
           public void start(Stage primaryStage) throws Exception {
                Group root = new Group();
                
                Circle outer = new Circle(100, 100, 100, Color.DARKBLUE);
                Circle inner = new Circle(100, 100, 90, Color.web("EFEFEF"));
                
                Line secHand = new Line(100, 100, 100, 20);
                secHand.setStroke(Color.RED);
                
                root.getChildren().addAll(outer, inner, secHand);
                          
                Rotate secRotate = new Rotate(0, 100, 100);
                secRotate.angleProperty().bind( sec.divide(60.0).multiply(360.0) );
                
                Thread thread = new Thread(new Runnable() {
                     public void run() {
                          while (true) {
                               setTime();
                               try {
                                    TimeUnit.SECONDS.sleep(1);
                               } catch (InterruptedException e) {
                                    e.printStackTrace();
                               }
                          }
                     }
                },"Clock Thread : NOT ON JavaFX Application Thread");
                thread.setDaemon(true);
                thread.start();
                
                secHand.getTransforms().add(secRotate);
                
                Scene scene = new Scene(root, 200, 200);
                primaryStage.setScene(scene);
                primaryStage.show();
           }
           
           public void setTime() {
                System.out.println(Thread.currentThread().getName());
                calendar.setTimeInMillis(System.currentTimeMillis());
                sec.set( calendar.get(Calendar.SECOND ));
           }
      
           public static void main(String[] args) {
                launch(args);
           }
      }
        • 1. Re: Is this program thread safe?
          jsmith
          Do I have to call Platform.runLater() ?
          Yes.

          Though using your own thread will work fine, you may want to use a Timeline rather than create your own thread, then you don't have to worry about stuff like Platform.runLater().

          You can find an example of a clock with a Timeline here =>
          https://gist.github.com/2658491 "Sample of an animated clock in JavaFX"
          • 2. Re: Is this program thread safe?
            935477
            Thank you very much for the clarification.
            The example code was very helpful.

            So, basically JavaFX doesn't tell me if I wrongly access nodes in another thread?

            And one more question, do I have to call timeline.play() "in Java FX Application thread" if timeline has a KeyValue which is associated with Nodes property ?
            • 3. Re: Is this program thread safe?
              jsmith
              So, basically JavaFX doesn't tell me if I wrongly access nodes in another thread?
              Sometimes it does, sometimes it doesn't.
              Here is an example of what happens when the system detects an operation off the JavaFX Application Thread which should only happen on the JavaFX Application Thread (it throws an exception)
              // output:
              //
              // java.lang.IllegalStateException: Not on FX application thread; currentThread = JavaFX-Launcher
              // and a stack trace for each illegal access.
              
              import javafx.application.Application;
              import javafx.scene.control.*;
              import javafx.scene.web.WebView;
              import javafx.stage.Stage;
              
              // demonstrates RT-17716: Some controls can only be created on the FX application thread
              public class InitThreadTest extends Application {
                public static void main(String[] args) { Application.launch(args); }
                @Override public void init() throws Exception {
                  new Button();
                  try { new WebView(); } catch (Exception e) { e.printStackTrace(); }
                  try { new Tooltip(); } catch (Exception e) { e.printStackTrace(); }
                  try { new ContextMenu(); } catch (Exception e) { e.printStackTrace(); }
                }
                @Override public void start(Stage s) { System.exit(0); }
              }
              There may be plans to add more checks in more places, but I don't think there will ever be full coverage due to potential issues with performance, cluttering up the codebase, determining all the edge cases etc.
              do I have to call timeline.play() "in Java FX Application thread" if timeline has a KeyValue which is associated with Nodes property ?
              This is a very interesting question.
              No, you don't have to call timeline.play() on the JavaFX Application Thread.
              Irrespective of what thread the timeline was created in and play called from, the keyframe callback (and I would also surmise KeyValue manipulation) only happens on the Java FX Application thread.
              // output:
              //
              // Main thread: Thread[main,5,main]
              // Init thread: Thread[JavaFX-Launcher,5,main]
              // FX thread: Thread[JavaFX Application Thread,5,main]
              // My thread: Thread[Thread-3,5,main]
              // Init launched timeline callback thread: Thread[JavaFX Application Thread,5,main]
              // My thread launched timeline callback thread: Thread[JavaFX Application Thread,5,main]
              
              import javafx.animation.*;
              import javafx.application.*;
              import javafx.event.*;
              import javafx.stage.Stage;
              import javafx.util.Duration;
              
              public class TimelineThreadTest extends Application {
                public static void main(String[] args) throws InterruptedException {
                  System.out.println("Main thread: " + Thread.currentThread());
                  Application.launch();
                }
              
                @Override public void init() throws Exception {
                  System.out.println("Init thread: " + Thread.currentThread());
                  // print the timeline callback thread name after a second.
                  Timeline t = new Timeline(new KeyFrame(Duration.seconds(1), new EventHandler<ActionEvent>() {
                    @Override public void handle(ActionEvent actionEvent) {
                      System.out.println("Init launched timeline callback thread: " + Thread.currentThread());
                    }
                  }));
                  t.play();
                }
              
                @Override public void start(final Stage stage) throws Exception {
                  System.out.println("FX thread: " + Thread.currentThread());
                  stage.setWidth(5); stage.setHeight(5);
                  stage.show();
              
                  // create a new thread test timeline processing.
                  new Thread() {
                    @Override public void run() {
                      System.out.println("My thread: " + Thread.currentThread());
                      Timeline t = new Timeline(new KeyFrame(Duration.seconds(1), new EventHandler<ActionEvent>() {
                        @Override public void handle(ActionEvent actionEvent) {
                          System.out.println("My thread launched timeline callback thread: " + Thread.currentThread());
                        }
                      }));
                      t.play();
                      try { Thread.sleep(2000); } catch (InterruptedException e) { /** no action required */ }
                      Platform.runLater(new Runnable() { @Override public void run() { stage.close(); } });
                    }
                  }.start();
                }
              }
              Note that the callback to the FX application thread can only be setup and occur once the JavaFX system has been launched.
              This means Timelines are not useful outside of an FX application (e.g. for simulations or in a headless testing system) - which is unfortunate.
              // output:
              //
              // My thread: Thread[Thread-0,5,main]
              // Timeline is never executed even though play is called on it.
              
              import javafx.animation.*;
              import javafx.event.*;
              import javafx.util.Duration;
              
              public class TimelineMainTest {
                public static void main(String[] args) throws InterruptedException {
                  // print the timeline callback thread name after a second.
                  Timeline t = new Timeline(new KeyFrame(Duration.seconds(1), new EventHandler<ActionEvent>() {
                    @Override public void handle(ActionEvent actionEvent) {
                      System.out.println("Timeline thread: " + Thread.currentThread());
                    }
                  }));
                  t.play();
              
                  // create a new thread to wait a couple of seconds and prevent the main thread from shutting
                  // down until we are sure the timeline thread has been given a chance to take action.
                  new Thread() {
                    @Override public void run() {
                      try { Thread.sleep(2000); } catch (InterruptedException e) { /** no action required */ }
                      System.out.println("My thread: " + Thread.currentThread());
                    }
                  }.start();
                }
              }
              • 4. Re: Is this program thread safe?
                935477
                Thank you for the great explanation.

                Timeline seems useful although it's little hard to test.