I'm writing a program in Java which uses genetic algorithm to split one set of numbers into two sets so their's sum is equal or as close to equal as possible. The program is supposed to append results of each iteration in TextArea. I didn't want the program to freeze while doing computations so I put the logic in another thread. To be more specific:
- I created class Runner which extends Task. BestSpecimen is my class and it is used here to print iteration number and list of best solution(s) found in this iteration. Also it updates ProgressBas but it's not that important. I did it by adding following line in my controller class: runnerProgressPB.progressProperty().bind(runner.progressProperty());
- I wanted to do the same with updating TextArea but it didn't work properly. The point was that it updated TextArea with current iteration but deleted results from previous iterations.
- I used different approach: in my controller class I added this piece of code:
runner.valueProperty().addListener((observable, oldValue, newValue) -> { resultsTA.appendText(newValue + "\n"); });
- Simple, it is just listening for changes of ValueProperty in thread called runner. I update the ValueProperty after creating BestSpecimen object which happens in the middle of the iteration. However, here comes the problem - many iterations are missing from TextArea. Take a look at the screenshot. There are over 100 iterations missing at one point! Screenshot
- Well, what I think is happening? The thread runs so fast that TextArea can't update info on time. I put some Thread.sleep() inside the for loop which reduced number of missing iterations but the program runs terribly slow! I used Thread.sleep(20) and it took 5-10 minutes to finish and there were still some missing iterations. That's not the solution.
- What I want to achieve? I want the runner thread to suspend after setting ValueProperty and wait until TextArea is not updated. I think I should use wait() / notify() construction but dunno where exactly. I'm pretty new in thread things...
Here's the main loop in call() method of my Runner class. Before it, there are only declarations of variables.
while (!this.done) { for (int i = 0; i < this.iterations; i++) { current = this.population.get(i); theBestOnes = current.getTheBestSpecimen(); bestCase = new BestSpecimen(i+1, theBestOnes); this.updateValue(bestCase); this.updateProgress(i, iterations); next = Population.createParentPopulationFromExistingOne(current); next.fillPopulation(); this.population.add(this.population.size(), next); } done = true; } return null;
By the way, there is also this nasty null (see the screenshot above) which is appended to the TextArea. Any ideas how I can get rid off it? The return statement is reqiured at the end of the method, and I can't return BestSpecimen object with no content.
updateXXX(...)
methods are intended to set the value of a property, not to provide a stream of values. Consequently they may coalesce multiple calls to a single call (in particular this is likely to happen if you update the value multiple times between two adjacent frame renderings). You need to explicitly append to the text area on the FX application thread. (Note that a text area may not be the best option if your output is huge.) - James_D