This discussion is archived
1 2 Previous Next 23 Replies Latest reply: Apr 3, 2013 9:50 PM by KonradZuse RSS

Calender.getInstance() issue with ScheduledThreadPoolExecutor

KonradZuse Explorer
Currently Being Moderated
cross posted here http://www.coderanch.com/t/607695/java/java/Calender-getInstance-system-time-autoupdate#2775075

I have been trying to get the time, and update it(like making my own digital clock).


Okay, so I tried using the ScheduledThreadPoolExecutor, and it worked great! The only issue is that I'm having trouble updating the calender.

NOTE: Lambda expressions are used as "->" so runnable and run have been removed.
public class Timer extends Application
{
  //  Timer t = new Timer();
    Calendar rightNow =  Calendar.getInstance();
    @Override
    public void start(Stage primaryStage)
    {
        Button btn = new Button();
        
         ScheduledThreadPoolExecutor stpe = new ScheduledThreadPoolExecutor(1);
          System.out.println(stpe.getCorePoolSize());
          
                stpe.scheduleAtFixedRate(() -> {
                               btn.setText("" + rightNow.getTime());
                               System.out.println(rightNow.getTime());
   //rightNow = Calendar.getInstance();
                },0, 1,TimeUnit.SECONDS);
                
        btn.setOnAction((ActionEvent event) -> {
          //  rightNow = Calendar.getInstance();
        });
        
        StackPane root = new StackPane();
        root.getChildren().add(btn);
        
        Scene scene = new Scene(root, 300, 250);
        
        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }
Basically if I run it normally it will just return the instance at that one second, and wont update with rightNow.getTime() and will just pop out the same time constantly.

If I try to set
 rightNow = Calendar.getInstance();
inside the setFixedRate it wont run the scheduler, and if I click the button after I've seen it update the instance a few times, it will then stop right away.

Why exactly is my ScheduledThreadPoolExecutor breaking when I'm getting my instance? Is there another way I should go about this maybe?
  • 1. Re: Calender.getInstance() issue with ScheduledThreadPoolExecutor
    James_D Guru
    Currently Being Moderated
    You're running into problems because you're trying to update the UI (specifically the text of the button) from a thread other than the FX Application thread.

    Replace
    btn.setText("" + rightNow.getTime());
    with
    Platform.runLater(() -> btn.setText("" + rightNow.getTime()));
  • 2. Re: Calender.getInstance() issue with ScheduledThreadPoolExecutor
    James_D Guru
    Currently Being Moderated
    Actually, while the change I made makes this work (at least, on my setup); it still has issues. You're (now) accessing the variable rightNow from multiple threads, without any synchronization. There's no guarantee that the data will be updated correctly. A simple fix would be to update rightNow in the Platform.runLater(...) (so it is only accessed from the FX Application thread).

    So something like
    stpe.scheduleAtFixedRate(() -> {
       Platform.runLater( () -> {
         rightNow = Calendar.getInstance();      
         btn.setText("" + rightNow.getTime());
         System.out.println(rightNow.getTime());
       }
    },0, 1,TimeUnit.SECONDS);
  • 3. Re: Calender.getInstance() issue with ScheduledThreadPoolExecutor
    KonradZuse Explorer
    Currently Being Moderated
    James_D wrote:
    Actually, while the change I made makes this work (at least, on my setup); it still has issues. You're (now) accessing the variable rightNow from multiple threads, without any synchronization. There's no guarantee that the data will be updated correctly. A simple fix would be to update rightNow in the Platform.runLater(...) (so it is only accessed from the FX Application thread).

    So something like
    stpe.scheduleAtFixedRate(() -> {
    Platform.runLater( () -> {
    rightNow = Calendar.getInstance();      
    btn.setText("" + rightNow.getTime());
    System.out.println(rightNow.getTime());
    }
    },0, 1,TimeUnit.SECONDS);
    You're the best, thanks soo much!

    Why exactly does it have to run on the "FX Thread?" I thought that only happens when we are mixing a Swing Application with FX, and have to use different thread?

    Is it because runnable itself is not part of the FX Threadpool?

    Also I thought since FX is being built into the JDK, we wouldn't have to worry about these things.. GRRRR?!?


    On the Ranch a user posted "And, of course, you are NOT suposed to to update the Calender.getInstance(), instead you should get a new instance every second. "

    But isn't that what we are doing?

    Also is it weird that the first time I ran it, the 1st "second" marker was the same as the 2nd "second" marker, "i.e., 5:33:03 happened twice" then it went to :04,05, etc... The second time I ran it, it was fine... Thoughts?

    Edited by: KonradZuse on Mar 23, 2013 2:33 AM
  • 4. Re: Calender.getInstance() issue with ScheduledThreadPoolExecutor
    James_D Guru
    Currently Being Moderated
    KonradZuse wrote:

    Why exactly does it have to run on the "FX Thread?" I thought that only happens when we are mixing a Swing Application with FX, and have to use different thread?

    Is it because runnable itself is not part of the FX Threadpool?
    Yes, sort of. FX Applications start up a thread of execution (the FX Application thread). All changes to the UI must be made on this thread. If you don't you could get unpredictable behavior: for example, imagine you change the UI halfway through the rendering the screen; you could get a screen partially rendered with old data and partially rendered with new data. The JavaFX classes are written on the assumption that you only make these changes on the FX Application thread, so violating this could actually cause thread deadlock (two threads both waiting for each other to do something), which looks like what you had here.

    You start a new thread with your ScheduledThreadPoolExecutor, and you tried to modify the button's text on that thread.
    Also I thought since FX is being built into the JDK, we wouldn't have to worry about these things.. GRRRR?!?
    Not sure where you got that. The JDK provides classes to make this as easy as possible, but if you use multiple threads you always have to think about these issues to some extent. All FX event handling is on the FX Application thread, so if you don't actually start background threads yourself you have no issues.
    On the Ranch a user posted "And, of course, you are NOT suposed to to update the Calender.getInstance(), instead you should get a new instance every second. "

    But isn't that what we are doing?
    You are getting a new instance every time. What the poster may have meant is that you shouldn't update the reference (rightNow) from one thread and access it from another, which you were originally doing. My fix was to move the update of the reference to Platform.runLater(...), so you are now only accessing that variable from one thread. You could actually remove that variable altogether, though, which would probably be safer (you'd be less likely to introduce bugs later). Just do btn.setText(Calendar.getInstance().getTime().toString()) in the Platform.runLater().
  • 5. Re: Calender.getInstance() issue with ScheduledThreadPoolExecutor
    jsmith Guru
    Currently Being Moderated
    You might be interested in the digital clock sample here:
    https://gist.github.com/jewelsea/3388637

    The sample makes use of the JavaFX animation framework. The reason I like the linked sample is because it does not spawn a seperate user thread for performing the clock updates. That means that the application developer does not need to worry about the threading issues discussed in this forum thread (because everything is run on the JavaFX Application thread).
    import javafx.animation.Animation;
    import javafx.animation.KeyFrame;
    import javafx.animation.Timeline;
    import javafx.event.ActionEvent;
    import javafx.event.EventHandler;
    import javafx.scene.control.Label;
    import javafx.util.Duration;
     
    import java.util.Calendar;
     
    /**
     * Creates a digital clock display as a simple label.
     * Format of the clock display is hh:mm:ss aa, where:
     *   hh Hour in am/pm (1-12)
     *   mm Minute in hour
     *   ss Second in minute
     *   aa Am/pm marker
     * Time is the system time for the local timezone.
     * see: digital-clock.css for css formatting rules for the clock.
     */
    public class DigitalClock extends Label {
      public DigitalClock() {
        setId("digitalClock");
     
        getStylesheets().add(
          ResourceResolver.getResourceFor(
            getClass(),
              "digital-clock.css"
          )
        );
     
        bindToTime();
      }
     
      // the digital clock updates once a second.
      private void bindToTime() {
        Timeline timeline = new Timeline(
          new KeyFrame(Duration.seconds(0),
            new EventHandler<ActionEvent>() {
              @Override public void handle(ActionEvent actionEvent) {
                Calendar time = Calendar.getInstance();
                String hourString   = StringUtilities.pad(2, ' ', time.get(Calendar.HOUR) == 0 ? "12" : time.get(Calendar.HOUR) + "");
                String minuteString = StringUtilities.pad(2, '0', time.get(Calendar.MINUTE) + "");
                String secondString = StringUtilities.pad(2, '0', time.get(Calendar.SECOND) + "");
                String ampmString   = time.get(Calendar.AM_PM) == Calendar.AM ? "AM" : "PM";
                setText(hourString + ":" + minuteString + ":" + secondString + " " + ampmString);
              }
            }
          ),
          new KeyFrame(Duration.seconds(1))
        );
        timeline.setCycleCount(Animation.INDEFINITE);
        timeline.play();
      }
    }
    
    public class StringUtilities {
      /**
       * Creates a string left padded to the specified width with the supplied padding character.
       * @param fieldWidth the length of the resultant padded string.
       * @param padChar a character to use for padding the string.
       * @param s the string to be padded.
       * @return the padded string.
       */
      public static String pad(int fieldWidth, char padChar, String s) {
        StringBuilder sb = new StringBuilder();
        for (int i = s.length(); i &lt; fieldWidth; i++) {
          sb.append(padChar);
        }
        sb.append(s);
    
        return sb.toString();
      }
    }
  • 6. Re: Calender.getInstance() issue with ScheduledThreadPoolExecutor
    James_D Guru
    Currently Being Moderated
    That's nice. I was trying to see a good way to do this with javafx.animation; the trick I was missing was using KeyFrames without any KeyValues. I'm sure there's a bunch of other javafx.animation based ways to do this too.
  • 7. Re: Calender.getInstance() issue with ScheduledThreadPoolExecutor
    KonradZuse Explorer
    Currently Being Moderated
    Thanks for the explanations.


    I meant that I thought the point of the whole FX 8 and JDK 8 was to integrate FX more into SE and such. I guess it will still NEED FX threads.

    Also I didn't think I needed an FXThread, because I thought I was using an FXThread the entire time(since I created an FX Applications). It makes sense to do what you suggested "Calender.getinstance.etc"



    I'm not sure if I should keep the ScheduledThreadPool, because I also want to implement an "countdown timer" that will start from lets say 60 seconds, and go down to 1. I figure I could have 1 thread dedicated to the current time, and one to the countdown timer. Who knows what will happen though. :)


    Thanks for the help, as always both of you!


    EDIT: Didn't realize that the above code was different from the stop watch... Would animation be better to use? I didn't think it would be good, so I looked elsewhere, but I guess if I keep updating calender.getInstance and that's all I do infinitely I guess it will be okay.

    Also I'm curious, in the watch sample there's a class called Watch and at one point it shows this image grabber
        public Image loadImage() {
            InputStream is = Watch.class.getResourceAsStream("stopwatch.png");
            return new Image(is);
        }
    The image is in the same project folder. I'm not too familar with the classpath itself, but it just seems to be where the path to the class/clases are? I guess since it's in the same package, it will find it with that name, is this something that you would recommend? I've seen a lot of different options from the "Image" JavaFX API.

    Also is there a reason why my Calendar.AM_PM field = 1? It says it's supposed to return AM or PM....

    AM = 1 and PM = 2013...?????


    Also what's up with Calendar.Long Short, and Narrow? It iddn't really make much sense... I got 2013 I think for Narrow as well..... Those 3 are from "1.8" which is the beta right? AM_PM do not specify, so it seems like it's from earlier.


    I see from the example above that they store the value as a string, so I might want to do that first...

    Edited by: KonradZuse on Mar 23, 2013 3:01 PM

    Edited by: KonradZuse on Mar 23, 2013 7:15 PM

    Edited by: KonradZuse on Mar 23, 2013 7:22 PM
  • 8. Re: Calender.getInstance() issue with ScheduledThreadPoolExecutor
    James_D Guru
    Currently Being Moderated
    I think you can still use the techniques in the code sample jsmith showed.

    Also, if you're using JDK8, there's a nice new time API that is much better than java.util.Calendar.

    For example:
    import java.time.Instant;
    import java.time.Duration; 
    
    import javafx.animation.KeyFrame;
    import javafx.animation.Timeline;
    import javafx.application.Application;
    import javafx.beans.binding.Bindings;
    import javafx.beans.property.IntegerProperty;
    import javafx.beans.property.SimpleIntegerProperty;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.control.Label;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    
    public class Timer extends Application {
    
         private static final Duration COUNTDOWN_LENGTH = Duration.ofSeconds(60) ;
         
         @Override
         public void start(Stage primaryStage) {
              final Label label = new Label("Press button to start");
              final Button button = new Button("Start");
              final IntegerProperty secondsRemaining = new SimpleIntegerProperty();
              label.textProperty().bind(
                        Bindings.when(secondsRemaining.greaterThan(0))
                        .then(Bindings.format("Seconds remaining: %d", secondsRemaining))
                        .otherwise("Press button to start"));
              button.setOnAction(event -> startTimer(secondsRemaining) );
              button.disableProperty().bind(secondsRemaining.greaterThan(0));
              VBox root = new VBox(5);
              root.getChildren().addAll(label, button);
              Scene scene = new Scene(root, 200, 50);
              primaryStage.setScene(scene);
              primaryStage.show();
         }
         
         private void startTimer(final IntegerProperty secondsRemaining) {
              final Instant finishTime = Instant.now().plus(COUNTDOWN_LENGTH);
              Timeline countdown = new Timeline(
                   new KeyFrame(javafx.util.Duration.ZERO, 
                             (event) -> secondsRemaining.set((int) Duration.between(Instant.now(), finishTime).getSeconds())
                    ),
                   new KeyFrame(javafx.util.Duration.seconds(1))
              );
              countdown.setCycleCount((int)COUNTDOWN_LENGTH.getSeconds());
              countdown.play();
         }
    
         public static void main(String[] args) {
              launch(args);
         }
    }
    Also, to your threading questions:

    Any JavaFX application starts up a thread, called the JavaFX application thread, which is responsible for event handling and UI code. In a JavaFX application, almost all the code you write will be executed on this thread, since it's either in the start() method, or in an event handler. In your original code, you passed a Runnable into a ScheduledThreadPoolExecutor. That class creates new threads for you, and executes your Runnable on them. So now you have multiple threads to worry about.

    The JavaFX animation API also creates new threads, but is JavaFX-friendly in that it provides places where you can supply handlers for various events, such as KeyFrames being reached and the animation finishing. These handlers are invoked on the FX Application thread for you, so you get back to the nice single-threaded feel of the code. Have a look at the "Threads" section in the [url http://docs.oracle.com/javafx/2/architecture/jfxpub-architecture.htm]Architecture and Framework JavaFX tutorial. The [url http://docs.oracle.com/javafx/2/threads/jfxpub-threads.htm]Concurrency and Threads tutorial may be useful too, but it's slightly different from the animation-based approach used here.
  • 9. Re: Calender.getInstance() issue with ScheduledThreadPoolExecutor
    KonradZuse Explorer
    Currently Being Moderated
    Makes sense, we need to specify which thread we are running, and the Platform.run is the FXThread.

    Thanks for the example, I didn't think you would create a countdown timer, I appreciate it.

    So the new API is "TIME?" I'm going to check that out, I hope it's much better than Calendar....

    I probably should have asked if I could do this with Animation and Timeline, as I was also unsure about KeyFrames and Keyvalues, and how each interact with the animation itself. I was looking at the animation tutorial, and have a few examples with it but I guess at the time I didn't know about Calendar.getInstance();


    I guess it was good to learn sometihng new, though I'm not sure if I will need ScheduledThreadPoolExecutor if I have The Animation and Timeline with FX?

    I will play around with that and post my results, thanks again for everything all :D.


    So I've been lookign at this time package(finally a package with useful goodies :p). Not too sure why Calendar, and Date were meh(Calendar was to replace date, but was still funky all over). Now we can actually do more things with clocks and time overall.. Creating an instance is just making some time in space? Could be now, could be in 20 years?


    This new API is cool, time to uncover it all :P

    Edited by: KonradZuse on Mar 24, 2013 5:05 PM

    Edited by: KonradZuse on Mar 25, 2013 10:56 AM
  • 10. Re: Calender.getInstance() issue with ScheduledThreadPoolExecutor
    KonradZuse Explorer
    Currently Being Moderated
    So there is a lot more than I need in the time package, and realized I should be using LocalTime, instead of Clock.

    The issue I run into is that it displays it using a 24-hour format, and I'm not too sure if there is a way to fix it... There also isn't a AM/PM option either...


    I tried to do this, but it isn't working.
     public void clock()
        {
            System.out.println(clock.getHour());
            if(clock.getHour() > 12)
    
            
                    {
                        
                        clock.minusHours(12);
                                System.out.println(clock.getHour());
                    }
                    
           // String s = clock.instant().toString();
            
            Timeline countdown = new Timeline(new KeyFrame(javafx.util.Duration.ZERO, (event) 
                    -> 
    
                    labelC.setText(clock.toString().substring(0, 8))
                    ),
                   new KeyFrame(javafx.util.Duration.seconds(1), (event) -> clock.plusSeconds(1)));
    
              countdown.setCycleCount(Timeline.INDEFINITE);
              countdown.play();  
        }
    neither minusHours nor plusSeconds work, I get 19 hours for both system prints.

    I know that I put
                    new KeyFrame(javafx.util.Duration.seconds(1), (event) -> clock.plusSeconds(1)));
    not too sure if it's supposed to work(It seems that doing an event @ 0 is to make it happen before the second tick, and 1 would be after the tick so it should still work)? For some reason the event is only one line, I thought it could be more, but I'll have to look into it.


    I know this API is new, but I havent' found any jira reports so anyone have any idea?
  • 11. Re: Calender.getInstance() issue with ScheduledThreadPoolExecutor
    James_D Guru
    Currently Being Moderated
    LocalTime instances are immutable; you need to reassign the reference each time. So you need something along the lines of
    LocalTime clock ;
    ...
    // update clock to current time:
    clock = LocalTime.now();
    To display it, use a [url http://download.java.net/jdk8/docs/api/java/time/format/DateTimeFormatter.html]DateTimeFormatter to format it as you need.
  • 12. Re: Calender.getInstance() issue with ScheduledThreadPoolExecutor
    KonradZuse Explorer
    Currently Being Moderated
    I was going to say I figured out that if I say
     clock = clock.plusSecond(1); 
    it will work, weird.

    So I basically have to watch out for "immutable" classes then huh? I really am not good with terminology and the like, but I have heard of the term before, just never ran into it. I'll have to watch for classes like that, that don't have constructors and only work on calling itself to update.
  • 13. Re: Calender.getInstance() issue with ScheduledThreadPoolExecutor
    James_D Guru
    Currently Being Moderated
    Nothing to do with whether you create them via constructors or via a factory method: immutable means the state of the object cannot be changed once the object is created.

    The trick is only to read the documentation for the method. For example, the [url http://download.java.net/jdk8/docs/api/java/time/LocalTime.html#plusHours(long)]LocalTime.plusHours say
    >
    Returns a copy of this LocalTime with the specified period in hours added.
    This adds the specified number of hours to this time, returning a new time.
    >
    which make it reasonably clear it returns a reference to a new object representing the new value, as opposed to changing the value of the current object.

    Immutability is generally a really good thing. Read Josh Bloch's "Effective Java" for some good insights.
  • 14. Re: Calender.getInstance() issue with ScheduledThreadPoolExecutor
    James_D Guru
    Currently Being Moderated
    Better to use

    clock = LocalTime.now();

    than

    clock = clock.plusSecond(1);

    If your system gets busy doing something, it's possible one iteration of your timeline could be delayed briefly. If you simply add a second each time, all those delays will eventually add up and your clock will lose time. Updating to the current time each iteration makes sure you abandon any previous delay.
1 2 Previous Next

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points