This discussion is archived
1 2 Previous Next 15 Replies Latest reply: Mar 6, 2013 8:50 AM by James_D RSS

concurrency and javafx.beans.property

990211 Newbie
Currently Being Moderated
I am building a MVC architecture and i'd like the model to be running independetly of the view/controller, so the model runs concurrently with the controller/view.

the following example is a very simplified build on how i have it:

controller:
public class controller extends AnchorPane implements Initializable{
     private ObjectProperty<Model> m;
     @FXML Label aLabel;
     
     public controller() throws Exception{
          this.m = new SimpleObjectProperty<Model>(this, "Model", null);
          FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("../view/View.fxml"));
          fxmlLoader.setController(this);
          fxmlLoader.setRoot(this);
          fxmlLoader.load();
     }
     @Override public void initialize(URL arg0, ResourceBundle arg1){}
     public void setGame(Model m) throws Exception{
          this.m.set(m);
          aLabel.textProperty().bind(this.m.get().getIntProperty().asString());
     }
     public void start(){
          //Method1:
          m.get().start();
          //Method2:
          Task<Void> task = new Task<Void>() {
               @Override public Void call() {
                    m.get().start();
                    return null;
               }
          };
          new Thread(task).start();
          //Method3:
          m.get().start();     //Model extends Thread and public void start() to protected void run()
          //Method4:
          m.get().start();     //Model extends Task<Void> and
                         //public void start() to protected Void call() throws Exception
          //Method5:          //calling any of the before ones on the controller that calls this one
     }
}
model:
public class Model extends Thread{
     IntegerProperty intProperty;
     
     public Model(){
          this.intProperty = new SimpleIntegerProperty(0);
     }
     public IntegerProperty getIntProperty(){
          return intProperty;
     }
     public void start(){
          while (true){
               this.intProperty.set(this.intProperty.get()+1);
          }
     }
}
Tried any of those and the results are:
-Method1: the view crashes and cannot be seen anything (model seems to be working since continues in the loop)
-Method2: when reaches the first this.intProperty.set(this.intProperty.get()+1); the task gets frozen and stops
-Method3: execution error on this.intProperty.set(this.intProperty.get()+1);
-Method4: same as Method3
-Method5: like before ones

how can i make the it work?
  • 1. Re: concurrency and javafx.beans.property
    James_D Guru
    Currently Being Moderated
    There are a few things wrong here.

    First, if you want the model to use a Thread, make sure you know how to use the Thread class. There's a decent section on concurrency at the [url http://docs.oracle.com/javase/tutorial/essential/concurrency/index.html]Java tutorial. What you need here is to have your Model class override the run() method, not the start() method. Then call the start() method, which will cause the run() method to be executed on a separate thread of execution. (You may be doing this in Method 3, I couldn't understand your comment.)

    This is probably just an artifact of your simplified version, but your run() method should block at some point. Multiple threads can be executed on the same processor, so your current implementation may hog that processor, making it impossible for the FX Application thread to do its stuff. For testing, throw in a Thread.sleep(...) call, wrapped in a try/catch block for the InterruptedException. I assume that the real application is waiting for something from the server, so there would be some "natural" blocking of the thread in that case.

    The important rule for the UI is that changes to the interface should only be performed on the FX Application thread. Assuming you have the implementation of your model correctly running on a background thread, you violate this with your binding. (The model sets its intProperty on the background thread; the binding causes the label's text to be changed on the same thread.) So to fix this your controller should listen for changes in the model's int property, and schedule a call to aLabel.setText(...) on the FX application thread using Platform.runLater(...). You want to make sure you don't flood the FX Application thread with too many such calls. Depending on how often the int property in the model is getting updated, you may need the techniques discussed in {thread:id=2507241}.

    The Task API from JavaFX provides nice mechanisms to call back to the JavaFX Application thread; however this is not really applicable in this case. The Task class encapsulates a one-off task that (optionally) returns a value and then completes, which is not what you're doing here.

    Here's a complete example; it's not broken out into separate FXML for the view and a controller, etc, but you can see the structure and break it down as you need.
    import javafx.application.Application;
    import javafx.application.Platform;
    import javafx.beans.property.IntegerProperty;
    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.Button;
    import javafx.scene.control.Label;
    import javafx.scene.layout.AnchorPane;
    import javafx.stage.Stage;
    
    public class ConcurrentModel extends Application {
    
      @Override
      public void start(Stage primaryStage) {
        final AnchorPane root = new AnchorPane();
        final Label label = new Label();
        final Model model = new Model();
        model.intProperty.addListener(new ChangeListener<Number>() {
          @Override
          public void changed(final ObservableValue<? extends Number> observable,
              final Number oldValue, final Number newValue) {
            Platform.runLater(new Runnable() {
              @Override
              public void run() {
                label.setText(newValue.toString());
              }
            });
          }
        });
        final Button startButton = new Button("Start");
        startButton.setOnAction(new EventHandler<ActionEvent>() {
          @Override
          public void handle(ActionEvent event) {
            model.start();
          }
        });
    
        AnchorPane.setTopAnchor(label, 10.0);
        AnchorPane.setLeftAnchor(label, 10.0);
        AnchorPane.setBottomAnchor(startButton, 10.0);
        AnchorPane.setLeftAnchor(startButton, 10.0);
        root.getChildren().addAll(label, startButton);
    
        Scene scene = new Scene(root, 100, 100);
        primaryStage.setScene(scene);
        primaryStage.show();
      }
    
      public static void main(String[] args) {
        launch(args);
      }
    
      public class Model extends Thread {
        private IntegerProperty intProperty;
    
        public Model() {
          intProperty = new SimpleIntegerProperty(this, "int", 0);
          setDaemon(true);
        }
    
        public int getInt() {
          return intProperty.get();
        }
    
        public IntegerProperty intProperty() {
          return intProperty;
        }
    
        @Override
        public void run() {
          while (true) {
            intProperty.set(intProperty.get() + 1);
            try {
              Thread.sleep(50);
            } catch (InterruptedException exc) {
              exc.printStackTrace();
              break;
            }
          }
        }
      }
    }
  • 2. Re: concurrency and javafx.beans.property
    990211 Newbie
    Currently Being Moderated
    thanks it worked nicely, seems that the problem was the bind, so changed them into listeners (without threads and such, bind was working nicely)

    the thread i was doing them properly since changed the name start into run (and call in the case of task) just seems that the thread did not like the bind

    and one last question, if i want to make the model to wait an action from the view (for examle click a button), is this the correct way of doing it or should i find a way with listeners (as it can be seen on my late posts, I am not very used to them...)
    controller:
    public class controller extends AnchorPane implements Initializable{
         private ObjectProperty<Model> m;
         @FXML Button aButton;
         
         //aButton clicked
         @FXML protected void click(ActionEvent event){
              if(m.get().clickedWait)
                   m.get().clicked = true;
         }
    }
    model:
    public class Model extends Thread{
         controller c;
         public boolean clicked;
         public boolean clickedWait;
         
         public Model(){
              clickedWait = false;
         }
         public void run(){
              while(true){
                   try {
                        Thread.sleep(50);
                        waitClick();
                        //do anything
                   }catch (InterruptedException exc) {
                        exc.printStackTrace();
                        break;
                   }
              }
         }
         private void waitClick(){
              clickedWait = true;
              while(!clicked){
                   Thread.sleep(50);
              }
              clicked = false;
              clickedWait = false;
         }
    }
  • 3. Re: concurrency and javafx.beans.property
    James_D Guru
    Currently Being Moderated
    You can't manipulate a variable (clicked) like that from multiple threads. It is highly error prone. I recommend reading the concurrency tutorial I linked earlier.

    Maybe this is just because you have simplified the example a lot, but your example now doesn't need to be multithreaded at all. You've set up a loop in the model, running on a background thread, that simply waits until the user performs some action in the UI. Then the loop progresses, presumably does something, and heads into a wait mode again. Why not simply provide a method in your model to perform the single action, and invoke it from the controller when the user performs the action? Then everything happens on the FX Application thread anyway. You don't need to get into threads for this example.
  • 4. Re: concurrency and javafx.beans.property
    990211 Newbie
    Currently Being Moderated
    yes, i simplified it a lot (imagine that in the comment //do anything is a very heavy processor using algorithm, with calls to another classes and is showing in realtime on the view what it is doing)

    well, forgot the locks on the variables, but it was a general idea on how to build it

    just would be wanting something that only the button will do something if the model is waiting the click (waitClick() function)
  • 5. Re: concurrency and javafx.beans.property
    James_D Guru
    Currently Being Moderated
    In this case, you still shouldn't multithread your model. Define the method in the model and have it return a value if necessary:
    public class Model {
      public Object doSomethingComplicated() {
       // ... this method takes a long time to run
       return someValue ;
      }
    }
    Now invoke it from your controller in response to a button press, but wrap the call to the model in a Task. This is exactly the purpose of the JavaFX Task class:
    public class Controller {
      @FXML
      private Button someButton ;
    
      private Model model ;
    
      public void initialize() {
        model = new Model();
        // ...
      }
    
      // handler for someButton
      @FXML
      public void handleButtonPress() {
        final Task<Object> task = new Task<Object>() {
          @Override
          public Object call() {
             return model.doSomethingComplicated();
          }
        };
        task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
          @Override
          public void handle(WorkerStateEvent event) {
             // get the result of executing the task:
             Object result = task.getValue();
             // update UI here
             // this method is invoked on the FX Application thread
          }
        });
        
        // launch the task on a background thread
        new Thread(task).start();
      }
    }
    If you need to update the UI while the task is progressing, there are various mechanisms for that. From inside the task's call method, you can call updateMessage(...) which will update a String message. You can bind to the task's messageProperty() so that a control (Label, for example) will display the message as it is updated. You can call updateProgress(...) to update the progress, and similarly bind to the task's progressProperty(). These methods will be executed on the FX Application thread for you. If you need to do something not supported by these, you can invoke Platform.runLater(...) and pass in a Runnable that updates the UI. See the [url http://docs.oracle.com/javafx/2/api/javafx/concurrent/Task.html]API for the Task class for some examples.
  • 6. Re: concurrency and javafx.beans.property
    990211 Newbie
    Currently Being Moderated
    sorry, i messed up my post, thought it after leaving home and was unable to edit it

    what i have, is a model that does things and at a moment (decided by the model) it needs to interact with the user (in this case just pressing a button, but could be something more sophisticated)
  • 7. Re: concurrency and javafx.beans.property
    James_D Guru
    Currently Being Moderated
    Hello World! wrote:
    sorry, i messed up my post, thought it after leaving home and was unable to edit it

    what i have, is a model that does things and at a moment (decided by the model) it needs to interact with the user
    In an MVC design, the model should know nothing about the UI. The model is just a representation of the data. If the UI needs to be updated as a result of changes in the data in the model, either bind to properties in the model, or register listeners with the model and update the UI in response. These bindings and/or listener registrations can be created by the controller.
    (in this case just pressing a button, but could be something more sophisticated)
    I don't understand this. You seem to be saying the model is pressing a button, but that doesn't make sense. If you mean that the model does something in response to the user pressing a button, that was my previous example. Can you state the question more clearly?
  • 8. Re: concurrency and javafx.beans.property
    990211 Newbie
    Currently Being Moderated
    sorry for not writting things clearly

    a example might make things easier. for example playing a card game agaist the AI:
    the model is running background (defining turns, AI plays etc.)
    at a given moment it is your turn, so you have to chose a card of your hand to play it (the button on the example), so the model have to make the view to notice it and wait to the user selects a card
  • 9. Re: concurrency and javafx.beans.property
    James_D Guru
    Currently Being Moderated
    If this is a player vs computer game, then surely the model only needs to do something when the player makes a move. Again, you don't need a Thread in your model.
    public class Model {
      public enum Status { WAITING, THINKING };
      private final ObjectProperty<Status> status ;
      // ...
    
      public Model() {
        status = new SimpleObjectProperty<Status>(Status.WAITING);
      }
    
      public Status getStatus() {
        return status.get();
      }
    
      public ReadOnlyObjectProperty<Status> statusProperty() {
        return status ;
      }
    
      public Move playerMove(Move playerMove) {
        status.set(Status.THINKING);
        updateGameStatus(playerMove);
        Move myMove = figureNextMove() ;
        updateGameStatus(Status.WAITING);
        return myMove ;
      }
    
      private void updateGameStatus(Move playerMove)  { ... }
    
      private Move figureNextMove() { ... }
    
    }
    public class Controller {
      @FXML private Button moveButton ;
      
      private Model model ;
     
      public void initialize() {
        model = new Model() ;
        model.statusProperty().addListener(new ChangeListener<Model.Status>() {
          @Override
          public void changed(final ObservableValue<? extends Model.Status> observable,final Model.Status oldValue, final Model.Status newValue) {
             Platform.runLater(new Runnable() {
               @Override
               public void run() {
                  // update UI on the basis of newValue
               }
            });
          }
        });
      }
    
      @FXML
      public void moveButtonPressed() {
         final Move move = figurePlayerMoveFromUI();
         final Task<Move> computerMoveTask = new Task<Move>() {
            @Override
            public Move call() {
               Move computerMove = model.playerMove(move);
               return computerMove ;
            }
         };
         task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
           public void handle(WorkerStateEvent event) {
              Move computerMove = task.getValue();
              // update UI based on computer move
           }
        });
        new Thread(task).start();
      }
    
      // ...
    }
    and you'll need a Move class to represent a move in the game...

    If it's a multi-player game, where each player has an instance of your application and the applications connect to a server, then it's somewhat more difficult.
  • 10. Re: concurrency and javafx.beans.property
    990211 Newbie
    Currently Being Moderated
    i see, thanks a lot

    I will analyze it a bit more

    the idea of the thread was mainly because
    -if want to do a all AI game then to be able to (since model will be playing by itself and the view will be just being updated everytime the AI does something that affects it)
    -a "round" is composed by players turns, but it might not be in order (for example, in round 1, it is played as: player 1->player 2->player 3->player 4, but in round 2: player2->player 3->player 1->player 4 or even one of the players not palying that turn)
  • 11. Re: concurrency and javafx.beans.property
    James_D Guru
    Currently Being Moderated
    Your plan is feasible, and the basic counter that I first posted would be a starting idea for that. However, I think it's probably more complex than simply letting the human player make a move, and then in response letting the model make all the moves until it's the human player's turn again. It probably depends on the complexity of the game.
  • 12. Re: concurrency and javafx.beans.property
    990211 Newbie
    Currently Being Moderated
    since turns are not so well defined, would the concurrent way or your example ones work better?

    or maybe this other way?
    public class controller extends AnchorPane implements Initializable{
         private ObjectProperty<Model> m;
         @FXML Button aButton;
         
         //aButton clicked
         @FXML protected syncronized void click(ActionEvent event){
              //do something so model can work with
              notify();
         }
         public syncronized void waitClick(){
              try{
                   this.wait();
              }catch (InterruptedException exc) {
                   exc.printStackTrace();
              }
         }
    }
    model:
    public class Model extends Thread{
         controller c;
         
         public Model(){
              clickedWait = false;
         }
         public void run(){
              while(true){
                   try {
                        Thread.sleep(50);
                        c.waitClick();
                        //do anything
                   }catch (InterruptedException exc) {
                        exc.printStackTrace();
                        break;
                   }
              }
         }
    }
  • 13. Re: concurrency and javafx.beans.property
    James_D Guru
    Currently Being Moderated
    That's a more correct version of having the Model driving a loop, which is an option. Whether you use that design or a design where the model responds to the human player making a move (effectively using FX's event handling instead of the model having a loop). Which you choose depends on how complex the structure of the game is. If it were chess, where there are two players and one takes a turn after the other, then the first option probably works well. If you're implementing something like a poker game, where players are eliminated, and can eliminate themselves from each round, then the latter might be a better choice. Up to you at this point....
  • 14. Re: concurrency and javafx.beans.property
    990211 Newbie
    Currently Being Moderated
    is it more like the poker one you mentioned (even more complex in the meaning of turn)
    so i suppose i will run a thread
    and last thing, the last piece of code i wrote (with wait and notify) should be the "correct" way on doing such implementation?

    thanks a lot for all your answers
1 2 Previous Next

Legend

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