GUI Takes Longer Than Calculation And Slows Entire Process - java

We have a complex calculation that takes a variable time. With some input values thousand steps can be done in one second - with other input values a step takes several seconds.
That's completely correct, so we just want to inform the user about the progress. The problem is that in the former case updating the GUI takes longer than the actual calculation, so after it is done there are still about 10 seconds of GUI update events in the queue (which in that case triples the execution time of the entire calculation).
I think that's a general problem, so I broke it down to a somewhat framework agnostic example:
public class QueueTest {
static final int STEPS = 30;
public static void main(String[] args) {
final Gui gui = // ...
final Display display = Display.getDefault();
final Thread thread = new Thread(() -> {
for (int i = 0; i < STEPS; i++) {
final int step = i; // calculate something etc.
gui.updateLater(display, step);
}
System.out.println("Finished calculation.");
});
thread.start();
while (true) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
interface Gui {
default void updateLater(Display display, int step) {
display.asyncExec(() -> update(step));
}
default void update(int step) {
System.out.println("Update " + (step + 1) + " / " + STEPS);
if (step == STEPS - 1) {
System.out.println("Finished GUI.");
}
}
}
}
(The additional Thread only "calculates" steps and sends it to the GUI to show the progress.)
So let's consider some implementations of Gui:
static class NoGui implements Gui {
#Override
public void update(int step) {
if (step == STEPS - 1) {
System.out.println("Finished GUI.");
}
}
}
This example prints only when the GUI is finished. The result is these two lines, printed at almost the same time:
Finished calculation.
Finished GUI.
That's perfectly reasonable. The GUI events are quick to be finished. Now let's make them slow:
static class SlowGui implements Gui {
#Override
public void update(int step) {
try {
Thread.sleep(100);
Gui.super.update(step);
} catch (final InterruptedException e) {
e.printStackTrace();
}
}
}
This prints something like the following, with the finishing of calculation and the GUI three seconds apart:
Finished calculation.
Update 1 / 30
Update 2 / 30
Update 3 / 30
...
Update 30 / 30
Finished GUI.
That's what I'm seeing in our application. The calculation finishes, but the GUI is too slow and has to execute its event queue after the calculation is already done.
I want to optimize this behavior and came up with something like this:
static class IgnorantGui extends SlowGui {
private boolean inProgress;
private Integer nextStep;
#Override
public void updateLater(Display display, int step) {
if (this.inProgress) {
this.nextStep = Integer.valueOf(step);
} else {
this.inProgress = true;
super.updateLater(display, step);
}
}
#Override
public void update(int step) {
try {
Integer currentStep = Integer.valueOf(step);
do {
super.update(currentStep.intValue());
currentStep = this.nextStep;
this.nextStep = null;
} while (currentStep != null);
} finally {
this.inProgress = false;
}
}
}
The output is the following four lines:
Finished calculation.
Update 1 / 30
Update 30 / 30
Finished GUI.
This implementation just ignores the events in between and so is a lot quicker. This is a valid solution to my problem.
I think this entire use case might be common, and maybe there is a more elegant solution. Or even some standard Java API to handle it. (Maybe some SWT / Eclipse Framework API, since that's what we're using.)
So... how to handle a GUI that takes longer to update than the calculation and so slows the application down?

One way that I use is to poll the background thread using a timer runnable in the UI thread. Use Display.timerExec for this:
display.timerExec(100, new Runnable() {
#Override
public void run() {
// TODO update UI from background thread details
// Run again
display.timerExec(100, this);
}
});
The background thread doesn't do any asyncExec calls it just maintains data that the UI thread can access.

don't know if I get it right but it seems that you are continuously updating the GUI. Try to add something like a counter that determines when the gui should be updated. Or if it isnt needed to see all steps try something like
static class SlowGui implements Gui {
#Override
public void update(int step) {
try {
if(step%5==0){
Gui.super.update(step);
}
} catch (final InterruptedException e) {
e.printStackTrace();
}
}
}
this should update every 5 steps.
And why is there a sleep in the update method?
Hope I could help you.

Related

Continue executing a thread while another thread is running in java

tl, dr;
I have a GUI thread that creates an object of another class (the seconds class has implemented Runnable, but here we don't execute the run() method, instead, we call a normal method) and calls a method. In that method, the first thread (current thread) is called again (to show sth on the LCD), then sends some data to the Internet, and waits 3 seconds for the server response. The problem is that the information is printed after 3 seconds. I know about the stack and program counter, but I wonder if there is another option that I can do my job.
I have the main method, which runs 3 threads (for short, I just write the requisite code. Tell me to add more, if needed):
public static void main(String[] args) throws UnknownHostException, InterruptedException {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
GUI.getInstance().setVisible(true); //GUI is singleton, using swing and JFrame
} catch (Exception e) {
e.printStackTrace();
}
}
});
MQTTConnection.getInstance().tryToConnect(); //It's the connection class, which has a thread (the thread is handled by a library that keeps the connection alive. I have no threads there) and is a singleton too.
Thread t1 = new Thread(new SendDataThread()); //A thread which sends some data every 20 seconds.
t1.start();
}
And in SendDataThread, I have a function that creates some random data and sends them (using the MQTTConnection class).
This is the SendDataThread:
public class SendDataThread implements Runnable {
public void sendLog() {
boolean serverOnline = false;
StringBuilder data = new StringBuilder();
data.append(createData());
GUI.getInstance().printNeutral(data.toString()); //Prints BLACK on a `JTextPane`
if(MQTTConnection.getInstance().publishLog(MQTTConnection.getInstance().MQTT_PUBLISH_ESP_SEND_LOG, data.toString())) //This line has a 3 second timeout. If the server doesn't respond, it will return false. I've added the 3 seconds timeout too. Please continue reading.
serverOnline = true;
if(serverOnline)
GUI.getInstance().printOK("Server Submitted"); //Prints in GREEN
else
GUI.getInstance().printProblem("Check your connection!"); //Prints in RED
GUI.getInstance().printNeutral("-------------------------------------------------");
}
#Override
public void run() {
while(true) {
sendLog();
try {
Thread.sleep(20000); //sleeps 20 about seconds
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//.....
}
And this is the 3 seconds timeout method, in MQTTConnection:
boolean publishLog(String topic, String data){
mqtt_responds = false;
publish(topic, data);
System.out.println("MQTT is connected");
long lastTime = System.currentTimeMillis();
while(System.currentTimeMillis() - lastTime < callback_timeout) {
if(mqtt_responds){
mqtt_responds = false;
System.out.println("Server submitted");
return true;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Timeout");
return false;
}
Till now, everything work right. The problem starts where I have a button in the GUI class, which the user can manually send random logs:
JButton sendLogBtn = new JButton("Send Log");
sendLogBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
SendDataThread sdt = new SendDataThread();
sdt.sendLog();
}
});
sendLogBtn.setBounds(10, 331, 89, 23);
panel.add(sendLogBtn);
This button creates an object of SendDataThread and calls the sendLog() method. The issue happens here: after sendLog() is called, sendLog(), calls this GUI thread again:
--> GUI.getInstance().printNeutral(data.toString()); //Prints BLACK on a `JTextPane`
But the log is printed after 3 seconds (After the sendLog() method has finished working, the timeout!)
How can I fix this?
In the button's actionPerformed you are calling sendLog. sendLog does exactly what you said, ie reports some logs and waits about 3 seconds (assuming callback_timeout is about equal to 3000).
To fix this, you need to make sure that the 3sec blocking is not on the EDT and also to make sure that the logs are instead posted on the EDT.
As a quick workaround you can do:
sendLogBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new Thread(() -> new SendDataThread().sendLog()).start();
}
});
and then, as always, post your logs in the EDT like for example:
SwingUtilities.invokeLater(() -> GUI.getInstance().printNeutral(...));
AND
SwingUtilities.invokeLater(() -> GUI.getInstance().printProblem(...));
AND
SwingUtilities.invokeLater(() -> GUI.getInstance().printOk(...));
As for the question in your comment, I don't really understand what you are asking, but I should say that (as far as I know) the EDT is a Thread where all the Swing code is (and should be) posted on for execution. This way the Swing code does not have to be synchronized, because all GUI related stuff is executed sequentially (on the EDT). AWT for example was not intended to be single threaded as far as I know. Swing is however single threaded.

How to stop a long running function

Consider this code:
class Solver {
private boolean abort = false;
public void solve(List<Case> cases) {
while(!abort) {
for(Case c : cases)
compute(c); // method that take too long to finish
}
}
// a bunch of methods
public void abort() {
abort = true;
}
}
// in another class
Solver solver = new Solver();
solver.solve(cases);
public void onSolveAborted() {
solver.abort();
}
How can I change this solution so I can abort the solve function instantly. I know I can implements the Runnable interface in Solver class so I can stop the thread. This will introduce many changes in our code and I don't know if the framework we are using allow creating threads.
This will not be possible without the use of threads. Something has to set abort() before the running thread will stop. Take a look at this example:
class Solver implements Runnable {
private List<Case> cases;
public Solver(List<Case> cases) {
this.cases = cases;
}
private void compute(Case c) {
try {
// Do some computation here
} finally {
// Sound the horns! Abandon ship!
}
}
public void solve(List<Object> cases) {
for (Case c : cases) {
try {
compute(c); // method that take too long to finish
} catch (InterruptedException e) {
// Hmm, maybe I should take the hint...
break;
}
}
}
public void run() {
solve(cases);
}
public static void main(String args[]) {
List<Case> cases = new ArrayList<Case>();
// Populate cases
Thread t = new Thread(new Solver(cases));
t.run();
do {
// Wait 30 seconds
t.join(30 * 1000);
// Not done yet? Lets drop a hint..
if(t.isAlive()) {
t.interrupt();
}
} while (t.isAlive());
}
}
Very simply, it launches solve in a thread. The main thread waits up to 30 seconds then interrupts solve method. The solve method catches the interruption and gracefully exits the computation. Unlike your solution using boolean abort, this launches an InterruptedException from anywhere in your thead code (and you should deal with the exception accordingly!) allowing you to halt execution at any time.
If you want more control, you can add the try.. catch inside compute so you can have a finally clause to close any opened files or whatnot. Perhaps better still, have a try.. finally in compute to deal with closing things in a "nice" way and the try.. catch (InterruptedException) in the solve method to handle what happens in the case of interruption (in short, cleanup logic and interruption logic don't have to be in the same method).
Do somthing like this
Let say, you have 100 cases, 10 has been solved and you want to abort remaing 90.
In your code, you are solving all the cases in one iteration, after that while loop check for abort.
public void solve(List<Case> cases) {
Iterator<Case> iterator = cases.iterator();
while (iterator.hasNext() && !abort) {
Case c=iterator.iterator.next();
compute(c);
}
}
Change your class to Runnable and use ExecutorService to run it. Then you can just use methods "shutDown()" or "shutDownNow()" methods. This is cleaner and less intrusive then what you suggested in your own question. Plus killing thread manually is a REALLY BAD idea. At some point in JDK itself in thread method "kill()" was killed as there is no clean way to do so properly

how do i add a number to an integer every second

I'm making a cookie clicker sort of game and I want a thing where every second a certain number let's say 5 is added to another number. So every second the integer variable is going up by 5. How would I create a sort of time measuring method where it measures time so I can add a number to another number.
public class timeTesting {
// I've put this method here for someone to create
// a timer thing
void timer()
{
}
public static void main(String[] args) {
// Original number
int number = 1;
// Number I want added on to the original number every 5 seconds
int addedNumber = 5;
}
}
You can use Timer to schedule a TimerTask who has the desired code inside the run() method. Check the code below (run() will be called once after 5000 milliseconds) :
Timer t = new Timer();
t.schedule(new TimerTask() {
#Override
public void run() {
number += addedNumber;
}
}, 5000);
Also you can use scheduleAtFixedRate(TimerTask task, long delay, long period) for repetitive tasks (here run will be called immediately, and every 5000 milliseconds):
Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
number += addedNumber;
}
}, 0, 5000);
If you're targeting the android platform you could use CountDownTimer, which allows you to execute some code every certain amount of time for a certain duration. But be aware that android doesn't work with the main method like J2SE does.
Anyway, if you're looking foward to program an android game, i'd highly recommend you to start here: Android Development
I'd like to suggest start studying RxJava. Reactive programming is very powerful for games development: https://www.youtube.com/watch?v=WKore-AkisY
With RxJava, your problem can be solved with Observable interval() method:
https://github.com/Netflix/RxJava/wiki/Creating-Observables#interval
Ivan
You should consider using a Timer which will fire an event when a certain time interval has passed. It can repeat the event every so often, too.
Not very elegant, but working code:
public class MyTimer {
private volatile int number; //must be volatile as we're working on multiple threads.
private final int numberToAdd;
private final long timerTimeInMillis;
public MyTimer() {
number = 1;
numberToAdd = 5;
timerTimeInMillis = 5000;
}
public void runTimer() {
new Thread() { //declaring a new anonymous Thread class
public void run() { //that has to override run method.
while (true) //run the program in an infinite loop
{
number += numberToAdd; //add a number
System.out.println("Added number. Now the number is: " + number);
try {
Thread.sleep(timerTimeInMillis); //and then sleep for a given time.
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start(); //and here we're starting this declared thread
}
public static void main(String[] args)
{
new MyTimer().runTimer();
try {
Thread.sleep(100000); //this application will work for 100sec.
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Using java.util.Timer would be more elegant, but in here you may get aquainted with anonymous classes.

Unable to update GUI while sending file

I am sending a file over network using sockets. The file is received properly without any problem. But now I am using a JProgressBar to show percentage of file sent. My problem is that even when I update GUI in a separate thread, the progress bar is updated only when file is completely sent. I also tried adjusting the priority of main thread to Thread.MIN_PRIORITY but the problem still persisted.
The complete code is long so I am not posting it (I will post if someone asks). The short code for sending file and updating progress bar is
final double temp=(done/fileSize)*100; // percentage of file sent
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
try
{
jpb.setString("Completed : "+temp+" %");
jpb.setValue((int)temp); // this is executed only when 100%
}
catch(Exception e)
{
e.printStackTrace();
}
}
});
System.out.println(temp); // only this and
bos.write(b,0,read); // this statement is executed
Problem lies in below line:
final double temp=(done/fileSize)*100; // percentage of file sent
If done and fileSize are both not double then result of done/fileSize is 0.0.
Make them double (at least one of them) to keep the decimal part of the division.
Here is a implementation that I talked about.
It's not the best design as I did it quick and dirty but this way the file transfer code is not dependent on the UI.
public class FileTransfer implements Runnable
{
double transferPercent = 0.0;
double getTransferPercent(){ return transferPercent; }
#Override
public void run()
{
while(transferingFile)
{
// Write data
// Update transferPercent
}
}
}
public class UIClass extends TimerTask
{
private FileTransfer fileTransfer;
public void createUI()
{
TimerTask myClass = this;
JButton b = new JButton("Transfer");
b.addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent e)
{
fileTransfer.start();
Timer t = new Timer();
t.scheduleAtFixedRate(myClass, 0.0, 20);
}
});
}
// Update the UI here!
#Override
public void run()
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
jpb.setValue(fileTransfer.getTransferPercent());
}
});
}
}
I'd probably design this differently. I'd make the network code independent of the UI and have it just update a variable on percentage sent.
The UI would then poll this number with a timer to update the progress bar.
But your code should work. Try adding an #Override on your run function. Maybe SwingUltities.invokeLater is calling Runnable's run function instead of your own.

Can i use Thread isAlive method to change a state in my runnable to execute code?

I have two buttons and when the user click the first one it will start a thread to update the UI, and when the user click the second one the app will set a boolean variable for the first thread to not allow it to update the thread and then it will start the second one. This is my Runnable :
class MyRunnable implements Runnable {
boolean isUpdateEnabled = true;
#Override
public void run() {
// Retrieve list from Internet. takes about 10 sec to complete.
if (isUpdateEnabled) {
runOnUiThread(new Runnable() {
public void run() {
System.out.print("Update UI");
}
});
}
}
void enableUpdate(boolean enable) {
isUpdateEnabled = enable;
}
}
but since the thread will take a time to complete, if the user press the first button again can i check if the first thread is alive and then enable it to update the thread while guarantee that the code inside if (isUpdateEnabled) will execute if or what the right way to do it?
...
// on button click
if(thread.isAlive())
runnable.enableUpdate(true);
...
I suppose you will need to do two things here :
Synchnorize access to isEnableUpdate, because two different threads are accessing it.
You may want to break your code in public void run into smaller chunks, and check the isEnableUpdate variable every once in a while when you finish some logical piece of work or something like that.
Sample :
class MyRunnable implements Runnable
{
boolean isUpdateEnabled = true;
Object myUpdateLockObj = new Object();
#Override
public void run() {
// Retrieve list from Internet. takes about 10 sec to complete.
if (isUpdateEnabled) {
runOnUiThread(new Runnable() {
public void run() {
lock(myUpdateLockObj)
{
if (!isUpdateEnabled) return;
}
//do work in parts
System.out.print("Update");
lock(myUpdateLockObj)
{
if (!isUpdateEnabled) return;
}
// do work in parts
System.out.print(" UI");
}
});
}
}
void enableUpdate(boolean enable)
{
synhronized(myUpdateLockObj)
{
isUpdateEnabled = enable;
}
}
}

Categories

Resources