i have a JSF web application deployed under glassfish in which i have two buttons.The first start a infinite thread and the second stop it.My problem is that i can not stop a running thread.I have searched for a solution on the net but in vain.it works in case i have a J2SE application but not with a J2EE application here is my code
package com.example.beans;
import org.apache.commons.lang.RandomStringUtils;
public class MyBusinessClass {
public static void myBusinessMethod() {
/* this method takes a lot of time */
int i = 1;
while (i == 1) {
String random = RandomStringUtils.random(3);
System.out.println(random);
}
}
}
package com.example.beans;
import java.util.Random;
import java.util.TimerTask;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.log4j.Logger;
import com.example.core.RandomUtils;
public class MySimpleRunnableTask implements Runnable {
private Logger logger = Logger.getLogger(MySimpleRunnableTask.class);
#Override
public void run() {
MyBusinessClass.myBusinessMethod();
}
}
#ManagedBean(name = "MainView")
#SessionScoped
public class MainView {
private static Thread myThread;
#SuppressWarnings({ "unchecked", "rawtypes", "deprecation" })
public String startSimpleThread() throws SecurityException,
NoSuchMethodException,
InterruptedException {
MySimpleRunnableTask mySimpleRunnableTask = new MySimpleRunnableTask();
myThread = new Thread(mySimpleRunnableTask);
myThread.start();
return null;
}
#SuppressWarnings({ "unchecked", "rawtypes", "deprecation" })
public String stopSimpleThread() throws SecurityException,
NoSuchMethodException,
InterruptedException {
myThread.interrupt();
return null;
}
}
I have changed my code so you can understand really what's my problem
interrupt only sets the interrupt status in the thread to true. The thread needs to regularly pool the interrupt status flag to stop running:
public void run() {
/* you will have to touch the code here */
int i = 1;
while (i == 1) {
String random = RandomStringUtils.random(3);
logger.info(random);
if (Thread.currentThread().isInterrupted()) {
// the thread has been interrupted. Stop running.
return;
}
}
}
This is the only way to properly stop a thread : ask him to stop. Without cooperation from the running thread, there is no clean way.
Related
I have a ManagedBean that can call a Service that accesses the database. That service has the #Service and #Transactional annotations, and since it is a service that has functions to mess around in the database, it obviously is called in a number of places.
In my frontend, in my managed bean I want to call for a function that is like a job, a batch, and it can take quite a while. The job can take like 1 minute and I don't want the client to be waiting on the frontend that long, since there may be jobs that can take like an hour.
I'll post an example of the code I have currently (just consider I have in my xhtml file some button that calls the launchBatch() function):
#ManagedBean
#RequestScoped
public class JobLauncherController {
#ManagedProperty("#{serviceController}")
private ServiceController serviceController;
public void launchBatch(String code) {
switch (code) {
case Constants.Batch.BATCH_1:
Thread t1 = new Thread(new Runnable() {
public void run() {
serviceController.getBatchService().batch1();
}
});
t1.start();
break;
case Constants.Batch.BATCH_2:
Thread t2 = new Thread(new Runnable() {
public void run() {
Date date = new Date();
String parameters = String
.valueOf(date.getHours() + ":" + date.getMinutes() +
":" + date.getSeconds());
serviceController.getBatchService().batch2(parameters);
}
});
t2.start();
break;
case Constants.Batch.BATCH_3:
Thread t3 = new Thread(new Runnable() {
public void run() {
serviceController.getBatchService().batch3();
}
});
t3.start();
break;
case Constants.Batch.BATCH_4:
Thread t4 = new Thread(new Runnable() {
public void run() {
serviceController.getBatchService().batch4();
}
});
t4.start();
break;
default:
break;
}
showGrowl();
}
public void showGrowl() {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "",
"Job launched sucessfully!");
FacesContext.getCurrentInstance().addMessage("growlPanel", message);
}
public ServiceController getServiceController() {
return serviceController;
}
public void setServiceController(ServiceController serviceController) {
this.serviceController = serviceController;
}
}
My service (interface):
public interface AppBatchService {
public void batch1();
public void batch2(Object parameters);
public void batch3();
public void batch4();
}
And the implementation
#Service
#Transactional
public class AppBatchServiceImpl implements AppBatchService {
//code to implementation goes here
}
So this is launching a thread successfully, because when I debug this, I have put the functions batchN to sleep for like a minute, and the code keeps flowing to my showGrowl() and my showGrowl(), ends the code flow successfully, the problem is at frontend, because it doesn't complete the action of the button while the thread I launched is running.
Is there a specific way to start threads when dealing with JSF/Primefaces to end the action, without changing the service I have? I've seen solutions with #Stateless and with #Asynchronous on the functions, but I cannot change this structure.
I try to high frequently update the cells in a JFX TableView (proof of concept application). I load a TableView via FXML and start an ExecutorService to change the value of a cell.
When I start the application I notice, that the update works for the first 3-4 million elements and then it stucks. If I slow down the updates (see MAGIC#1) it works (10ms is still too fast, but 100ms delay works). So I thought it might be a threading issue.
But then I found out that if I add an empty ChangeListener (see MAGIC#2) to the property it works fine. Even without the need of MAGIC#1.
Am I doing something wrong? Do I have to update the cells in a different way?
Thanks in advance for your help!!
The elements in the TableView:
public class Element {
public static final AtomicInteger x = new AtomicInteger(0);
private final StringProperty nameProperty = new SimpleStringProperty("INIT");
public Element() {
// MAGIC#2
// this.nameProperty.addListener((observable, oldValue, newValue) -> {});
}
public void tick() {
this.setName(String.valueOf(x.incrementAndGet()));
}
public String getName() ...
public void setName(String name)...
public StringProperty nameProperty() ...
}
The controller for FXML:
public class TablePerformanceController implements Initializable {
private final ObservableList<Element> data = FXCollections.observableArrayList();
public Runnable changeValues = () -> {
while (true) {
if (Thread.currentThread().isInterrupted()) break;
data.get(0).tick();
// MAGIC#1
// try { Thread.sleep(100); } catch (Exception e) {}
}
};
private ExecutorService executor = null;
#FXML
public TableView<Element> table;
#Override
public void initialize(URL location, ResourceBundle resources) {
this.table.setEditable(true);
TableColumn<Element, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(cell -> cell.getValue().nameProperty());
this.table.getColumns().addAll(nameCol);
this.data.add(new Element());
this.table.setItems(this.data);
this.executor = Executors.newSingleThreadExecutor();
this.executor.submit(this.changeValues);
}
}
You are violating the single threaded rule for JavaFX: updates to the UI must only be made from the FX Application Thread. Your tick() method updates the nameProperty(), and since the table cell is observing the nameProperty(), tick() results in an update to the UI. Since you're calling tick() from a background thread, this update to the UI happens on the background thread. The resulting behavior is essentially undefined.
Additionally, your code ends up with too many requests to update the UI. So even if you fix the threading issues, you need to somehow throttle the requests so that you don't flood the FX Application Thread with too many requests to update, which will make it unresponsive.
The technique to do this is addressed in Throttling javafx gui updates. I'll repeat it here in the context of a table model class:
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Element {
// Note that in the example we only actually reference this from a single background thread,
// in which case we could just make this a regular int. However, for general use this might
// need to be threadsafe.
private final AtomicInteger x = new AtomicInteger(0);
private final StringProperty nameProperty = new SimpleStringProperty("INIT");
private final AtomicReference<String> name = new AtomicReference<>();
/** This method is safe to call from any thread. */
public void tick() {
if (name.getAndSet(Integer.toString(x.incrementAndGet())) == null) {
Platform.runLater(() -> nameProperty.set(name.getAndSet(null)));
}
}
public String getName() {
return nameProperty().get();
}
public void setName(String name) {
nameProperty().set(name);
}
public StringProperty nameProperty() {
return nameProperty;
}
}
The basic idea here is to use an AtomicReference<String to "shadow" the real property. Atomically update it and check if it's null, and if so schedule an update to the real property on the FX Application Thread. In the update, atomically retrieve the "shadow" value and reset it to null, and set the real property to the retrieved value. This ensures that new requests to update on the FX Application thread are only made as often as the FX Application Thread consumes them, ensuring that the FX Application Thread is not flooded. Of course, if there is a delay between scheduling the update on the FX Application Thread, and the update actually occurring, when the update does happen it will still retrieve the latest value to which the "shadow" value was set.
Here's a standalone test, which is basically equivalent to the controller code you showed:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
public class FastTableUpdate extends Application {
private final ObservableList<Element> data = FXCollections.observableArrayList();
public final Runnable changeValues = () -> {
while (true) {
if (Thread.currentThread().isInterrupted()) break;
data.get(0).tick();
}
};
private final ExecutorService executor = Executors.newSingleThreadExecutor(runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t ;
});
#Override
public void start(Stage primaryStage) {
TableView<Element> table = new TableView<>();
table.setEditable(true);
TableColumn<Element, String> nameCol = new TableColumn<>("Name");
nameCol.setPrefWidth(200);
nameCol.setCellValueFactory(cell -> cell.getValue().nameProperty());
table.getColumns().add(nameCol);
this.data.add(new Element());
table.setItems(this.data);
this.executor.submit(this.changeValues);
Scene scene = new Scene(table, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I am trying to run an Infinite loop in my JavaFX app.
An infinite while loop is present in my code in the Kulta.java file.
This loop actually freezes my app.
While the same thing works when I port the app to normal javax.swing.
Now since java.lang.Thread doesn't work for javafx, I came accross javafx.concurrent.Task,
which is not working as intended. As one of the main features of multithreading, i.e. running an infinite loop in a GUI app, is not served properly, please help me with the solution.
This is my code:
Urania.java
import java.awt.Toolkit;
import java.awt.Dimension;
import javax.swing.SwingUtilities;
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;
public class Urania {
public static final Dimension DIMENSION = Toolkit.getDefaultToolkit().getScreenSize();
public static void main(String[] args) {
SwingUtilities.invokeLater(
new Runnable() {
#Override
public void run() {
Kulta kulta = new Kulta();
kulta.setTitle("Abha K Pauri");
kulta.setSize(DIMENSION.width/2, DIMENSION.height/2);
kulta.setLocationRelativeTo(null);
kulta.setDefaultCloseOperation(EXIT_ON_CLOSE);
kulta.setVisible(true);
}
}
);
}
}
And here is my JFrame in which I have embedded my JavaFX app.
Kulta.java
import javax.swing.JFrame
import javafx.embed.swing.JFXPanel;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.control.Button;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.concurrent.Task;
public class Kulta extends JFrame {
private JFXPanel fxpanel;
private Scene scene;
private BorderPane borderpane;
private Button button;
public static final String INVOKE = "INVOKE";
public static final String INTERRUPT = "INTERRUPT";
public static final String[] COLORS = new String[]{"yellow", "pink", "green", "blue", "orange"};
public Kulta() {
fxpanel = new JFXPanel();
add(fxpanel);
Platform.runLater(
new Runnable() {
#Override
public void run() {
Kulta.this.setScene();
Kulta.this.setButton();
Kulta.this.setListener();
}
}
);
}
private void setScene() {
borderpane = new BorderPane();
scene = new Scene(borderpane);
fxpanel.setScene(scene);
}
private void setButton() {
button = new Button(INVOKE);
borderpane.setTop(button);
}
private void setListener() {
Event event = new Event();
button.setOnAction(event);
}
private class Event implements EventHandler<ActionEvent> {
#Override
public void handle(ActionEvent event) {
boolean flag = true;
Task<Void> onInvoke = new Task<Void>() {
#Override
public Void call() {
int count = 0;
flag = true;
button.setText(INTERRUPT);
/* This loop freezes the app. */
while(flag) {
borderpane.setStyle("-fx-color: "+COLORS[count]+";");
count++;
if(count == COLORS.length)
count = 0;
}
return null;
}
};
Task<Void> onInterrupt = new Task<Void>() {
#Override
public Void call() {
button.setText(INVOKE);
if(flag)
flag = false; // This will stop the onInvoke thread
return null;
}
};
Task<Void> change = new Task<Void>() {
#Override
public Void call() {
if(button.getText().equals(INVOKE))
onInvoke().run();
else if(button.getText().equals(INTERRUPT))
onInterrupt().run();
}
};
change.run();
}
}
}
How should I write the loop in order to not let the app freeze.
Any code, solution, link or any help in any form will help a lot.
Thanks in advance.
I want to develop a Java program playing an mp3-file in a specific manner. I marked a number of fragments in this file with startTime and endTime. The program should play the first fragment and then sleep for 5 seconds. Then play the second fragment and sleep again. And so on. I use JavaFX class MediaPlayer. The program prototype is as follows:
import javafx.application.Application;
import javafx.stage.Stage;
import java.io.FileNotFoundException;
import java.io.IOException;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.util.Duration;
import java.util.concurrent.TimeUnit;
public class JavaFXMediaPlayer02 extends Application {
#Override
public void start(Stage stage) throws FileNotFoundException,IOException,InterruptedException {
Media media = new Media("file:///D:/1016_00.mp3");
MediaPlayer mediaPlayer = new MediaPlayer(media);
//Set and play the first fragment of mp3-file
mediaPlayer.setStartTime(Duration.millis(1219.0));
mediaPlayer.setStopTime(Duration.millis(2728.0));
mediaPlayer.play();
System.out.println("1st fragment played!");
TimeUnit.SECONDS.sleep(5);
//Set and play the second fragment
mediaPlayer.setStartTime(Duration.millis(3947.0));
mediaPlayer.setStopTime(Duration.millis(6629.0));
mediaPlayer.play();
System.out.println("2nd fragment played!");
TimeUnit.SECONDS.sleep(5);
//Set and play the second fragment
mediaPlayer.setStartTime(Duration.millis(7453.0));
mediaPlayer.setStopTime(Duration.millis(10704.0));
mediaPlayer.play();
System.out.println("3rd fragment played!");
}
public static void main(String[] args) {
launch(args);
}
}
But I only hear the 3rd fragment. What's the matter? Why don't I hear the first and the second fragments? How to correct my program? Isn't JavaFX an appropriate tool for my task?
The problem here lies in the TimeUnit.SECONDS.sleep(5); invokation. This method sets the current thread into sleep. And in your case this thread is the JavaFX Application Thread. That causes the whole application to "freeze" (which would be more obviously if you added some GUI-Elements) and therefore the mediaPlayer.play(); commands are executed, but are instantly "freezed" because of the sleep function. After the `TimeUnit.SECONDS.sleep(5); calls, you set new start and end times for your MediaPlayer and execute play() again, so that the track starts at the new start time. Thats why only your last fragment is played.
Now to the solution:
You should never invoke Thread.sleep() or similar methods on the JavaFX App Thread. But in your case you have to wait a certain amount of time between playing the fragments. The first approach would be invoke Thread.sleep() or TimeUnit.SECONDS.sleep(5); on a new thread and call the Mediaplayer methods on the JFX App Thread. But that doesn't work properly because you haven't set up an "order" in which the threads are called. There are various ways to do this (via Semaphores, Locks and Conditions, JavaFX Concurrency and so on...)
I tried to solve your problem by doing some quick-and-dirty programming, but i came across a problem with mediaPlayer.setStopTime(Duration.millis());. It does not seem to work on my computers, so that the files are always played to the end. I added a stop button to simulate the automatic stopping.
The following class sets the new start and endpoints and plays the fragment. If the mediaplayer is stops, it calls the next fragment on the LittleMediaScheduler class.
public class LittleMediaHelper implements Runnable {
public double startTime;
public double endTime;
public MediaPlayer player;
public int id;
public LittleMediaScheduler scheduler;
public LittleMediaHelper(double startTime, double endTime,
MediaPlayer player, int id) {
this.startTime = startTime;
this.endTime = endTime;
this.player = player;
this.id = id;
}
public LittleMediaScheduler getScheduler() {
return scheduler;
}
public void setScheduler(LittleMediaScheduler scheduler) {
this.scheduler = scheduler;
}
#Override
public void run() {
Platform.runLater(new Runnable() {
#Override
public void run() {
player.setStartTime(Duration.millis(startTime));
player.setStopTime(Duration.millis(endTime));
System.out.println(player.getStartTime());
System.out.println(player.getStopTime());
player.play();
player.setOnStopped(new Runnable() {
#Override
public void run() {
int idtmp = id + 1;
System.out.println("NEXT " + idtmp);
scheduler.call(idtmp);
}
});
}
});
}
}
This class is responsibly for sleeping a certain amount on a new thread and after successfully sleeping invoking the next LittleMediaHelper class play functionality.
public class LittleMediaScheduler {
private ArrayList<LittleMediaHelper> hArrL;
private int SLEEPTIME = 2000;
public LittleMediaScheduler(LittleMediaHelper... helpers) {
this.hArrL = new ArrayList<>();
for (LittleMediaHelper h : helpers) {
h.setScheduler(this);
System.out.println(h.startTime);
this.hArrL.add(h);
}
System.out.println(hArrL.size());
}
public void init() {
Thread t = new Thread(this.hArrL.get(0));
t.start();
}
public void call(final int id) {
Thread t = new Thread(new Task<String>() {
#Override
protected String call() throws Exception {
Thread.sleep(SLEEPTIME);
return null;
}
#Override
protected void succeeded() {
super.succeeded();
System.out.println("Next playing...");
if (id > LittleMediaScheduler.this.hArrL.size() - 1) {
return;
}
LittleMediaHelper next = LittleMediaScheduler.this.hArrL
.get(id);
Thread nextT = new Thread(next);
nextT.start();
}
});
t.start();
}
}
The main class with a stop button. Without mediaPlayer.pause() the player somehow repeats one step twice although new start end endpoints are set. Don't know if this is a bug or not.
public class JavaFXMediaPlayer02 extends Application {
#Override
public void start(Stage stage) throws FileNotFoundException, IOException,
InterruptedException {
Media media = new Media("file:///C:/test.mp3");
final MediaPlayer mediaPlayer = new MediaPlayer(media);
LittleMediaHelper phase1 = new LittleMediaHelper(0, 1000, mediaPlayer,
0);
LittleMediaHelper phase2 = new LittleMediaHelper(50000, 55000,
mediaPlayer, 1);
LittleMediaHelper phase3 = new LittleMediaHelper(200000, 200500,
mediaPlayer, 2);
LittleMediaScheduler scheduler = new LittleMediaScheduler(phase1,
phase2, phase3);
scheduler.init();
Group g = new Group();
Button b = new Button("STOP");
b.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
mediaPlayer.pause();
mediaPlayer.stop();
}
});
g.getChildren().add(b);
Scene sc = new Scene(g);
stage.setScene(sc);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I need to be able to call a separate thread. The thread analysis a file and pull stats from that.
The analyzing of the file can take up to 2 minutes and during the analysis data is printed to the logs.
I would like to have a TextArea on the front end that needs to print out the analysis (as it analysis) and I would also like to have a progress bar to indicate the progress. All of this is determined inside the separate thread.
What I have done is creating a method in the UI class to add a string to the Text Area and pass in a reference of this class to the launched thread.
My Main Class
package trymutilthread;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TryMutilThread extends Application {
TextArea ta;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Start");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
startScheduledExecutorService();
}
});
ta = new TextArea();
VBox vBox = new VBox();
vBox.getChildren().addAll(btn, ta);
StackPane root = new StackPane();
root.getChildren().add(vBox);
Scene scene = new Scene(root, 300, 750);
primaryStage.setScene(scene);
primaryStage.show();
}
private void startScheduledExecutorService() {
final TryMutilThread classI = this;
Task<Void> task = new Task<Void>() {
#Override protected Void call() throws Exception {
try {
ta.appendText("Starting Thread\n");
new SomeProcess(classI).doTheLogic();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
};
Thread th = new Thread(task);
th.setDaemon(true);
th.start();
}
public void appendText(String string) {
ta.appendText(string);
}
}
The class that is executed in the thread
package trymutilthread;
public class SomeProcess {
TryMutilThread taClass = null;
public SomeProcess (TryMutilThread taClass) {
this.taClass = taClass;
}
public void doTheLogic() throws Exception{
taClass.appendText("Staring Thread");
for (int i = 0; i < 5000; i++) {
taClass.appendText(String.valueOf(i));
}
taClass.appendText("Ending Thread");
}
}
Now when I execute this it still only output the text to the TextArea once the thread has ended.
I did had a look at the following 2 posts:
JavaFX update textArea
Java client / server thread null pointer exception when quickly communicating messages
I am not able to get the data printed to logs until the process has ended.
I updated my code to create a Task.
But now I am getting the following error when it executes
Executing com.javafx.main.Main from F:\DEV\Projects\TryMutilThread\dist\run404234128\TryMutilThread.jar using platform C:\Program Files\Java\jdk1.7.0_10/bin/java
java.lang.NullPointerException
at com.sun.javafx.sg.prism.NGTextHelper$TextAttributes.computeLinePadding(NGTextHelper.java:405)
at com.sun.javafx.sg.prism.NGTextHelper$TextAttributes.access$200(NGTextHelper.java:292)
at com.sun.javafx.sg.prism.NGTextHelper.buildTextLines(NGTextHelper.java:2357)
at com.sun.javafx.sg.prism.NGTextHelper.validateText(NGTextHelper.java:1847)
at com.sun.javafx.sg.prism.NGTextHelper.getCaretShape(NGTextHelper.java:1435)
at javafx.scene.text.Text.getDecorationShapes(Text.java:1150)
at javafx.scene.text.Text.impl_geomChanged(Text.java:757)
at javafx.scene.text.Text$1.invalidated(Text.java:214)
at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:127)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:161)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:67)
at javafx.scene.text.Text.setText(Text.java:188)
at com.sun.javafx.scene.control.skin.TextAreaSkin$17.invalidated(TextAreaSkin.java:610)
at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:359)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
at javafx.scene.control.TextInputControl$TextProperty.fireValueChangedEvent(TextInputControl.java:1034)
at javafx.scene.control.TextInputControl$TextProperty.markInvalid(TextInputControl.java:1038)
at javafx.scene.control.TextInputControl$TextProperty.invalidate(TextInputControl.java:978)
at javafx.scene.control.TextInputControl$TextProperty.access$200(TextInputControl.java:950)
at javafx.scene.control.TextInputControl$1.invalidated(TextInputControl.java:119)
at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:155)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
at javafx.scene.control.TextArea$TextAreaContent.insert(TextArea.java:196)
at javafx.scene.control.TextInputControl.replaceText(TextInputControl.java:373)
at javafx.scene.control.TextInputControl.insertText(TextInputControl.java:308)
at javafx.scene.control.TextInputControl.appendText(TextInputControl.java:298)
at trymutilthread.TryMutilThread.appendText(TryMutilThread.java:80)
at trymutilthread.SomeProcess.doTheLogic(SomeProcess.java:26)
at trymutilthread.TryMutilThread$2.call(TryMutilThread.java:66)
at trymutilthread.TryMutilThread$2.call(TryMutilThread.java:62)
at javafx.concurrent.Task$TaskCallable.call(Task.java:1259)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.lang.Thread.run(Thread.java:722)
I then google'd the error and it seems that I need to put the interaction code in a Platform.runlater().
Java client / server thread null pointer exception when quickly communicating messages
I changed the class to execute the thread to be
package trymutilthread;
import javafx.application.Platform;
public class SomeProcess {
TryMutilThread taClass = null;
public SomeProcess(TryMutilThread taClass) {
this.taClass = taClass;
}
public void doTheLogic() throws Exception {
taClass.appendText("Staring Thread");
for (int i = 0; i < 5000; i++) {
//remove this append line
//taClass.appendText(i + "\n");
//And replaced it with platform.runlater
Platform.runLater(new Runnable() {
#Override
public void run() {
taClass.appendText("AGREED" + "\n");
}
});
}
taClass.appendText("Ending Thread");
}
}
It executes without any errors but now it seems to be back to the start... The UI is frozen until all is added to the TextArea
The problem is just that you're flooding the FX Application Thread with too many requests; there's no actual work happening between your Platform.runLater(...) calls. This problem probably goes away with your real application instead of this test, but to mimic the actual long-running work, you can just put a Thread.sleep(...) in there:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class TextAreaBackgroundUpdateExample extends Application {
#Override
public void start(Stage primaryStage) {
final BorderPane root = new BorderPane();
final TextArea textArea = new TextArea();
final ProgressBar progress = new ProgressBar();
final Button startButton = new Button("Start");
final int maxCount = 5000 ;
startButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
for (int i = 1; i <= maxCount; i++) {
Thread.sleep(10);
final int count = i ;
Platform.runLater(new Runnable() {
#Override
public void run() {
textArea.appendText("Processed part " + count + " (of "+maxCount+")\n");
}
});
updateProgress(i, maxCount);
}
return null;
}
};
progress.progressProperty().bind(task.progressProperty());
Thread t = new Thread(task);
t.setDaemon(true);
t.start();
}
});
root.setCenter(textArea);
root.setTop(progress);
root.setBottom(startButton);
final Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}