I have a Java
thread with a run method that computes many things. You can think of it as a series of math statements as follows. Note that each computation may utilize other methods that in turn might have additional loops and such.
public void run() {
[computation 1 goes here here that takes a few seconds]
[computation 2 goes here that takes a few seconds]
....
[computation 30 goes here that takes a few seconds]
}
There is a GUI
that prints the output of these statements as they produce their results, and I'd like the user to be able to say "Stop" whenever they want. Here are two methods I thought of
Method 1: Many Boolean Checks [LOOKS TERRIBLE]
private boolean stop;
public void run() {
if(!stop)
[computation 1 goes here here that takes a few seconds]
if(!stop)
[computation 2 goes here that takes a few seconds]
....
if(!stop)
[computation 30 goes here that takes a few seconds]
}
In this method, once the stop flag has been set to true, the computations will end. Note how silly this looks, having 30 if
statements. Importantly, the critical question here is how often to put these conditions. Note that the computations on each line are not necessarily a single line. Taken to the extreme, does EVERY line in the run()
method deserve a if(!stop)
call above it? This doesn't seem like good design.
Method 2: Generalizing Computations [CANNOT DO THIS]
pirivate boolean stop;
public void run() {
for(int i = 0; i < computationsN && !stop; i++) {
computuations.get(i).compute();
}
I imagine that this method will be suggested, so I'd like to simply state that it is impossible in my case, given the sheer variation in the lines that I am calling "computations" for simplicity. I have typically done this for threads that are basic while
loops, and it works great for such. But not in this case when the run()
method is simply a huge method of variable code.
Any other solutions out there? Seems like this should be a universal problem. Thanks in advance!
what you want to do actually could be done with method 2, but you have to use the Strategy Pattern, is really the one more thing you need, because it make it possible to simplify your computations in one single line, like you actually did in Method 2.
It works like this, it lets you to change the next executable algorithm by doing polymorphism.
So first you have to make all your algorithms in different classes and each one has to implement one interface (it could be called Computable
) with one single method, that is, your compute()
method.
Ex.
public interface Computable {
public void compute();
}
And your Algorithms classes could be something like:
public class AlgorithmX implements Computable {
@Override
public void compute() {
// TODO Your Real Computation here for the Algorithm X
}
}
Then in your for
Loop your computations
Collection (or Array) is populated with Objects that implements Computable
, i.e., with your Algorithms Objects.
for(int i = 0; i < computations && !stop; i++) {
computuations.get(i).compute();
}
So you are in the right path with Method 2, I hope your way is more clear now.
Cheers!
Instead of using a stop
flag you can call interrupt()
on the thread to halt it, and inside your run
method check to see if (Thread.interrupted())
is true (interrupt()
doesn't immediately halt the thread, you've still got to check for Thread.interrupted()
). This way you avoid pitfalls like forgetting to declare your flag as volatile
.
http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.html#interrupt%28%29
Short of encapsulating all of your computations in Runnables
then putting them in an array and looping over them, which you've ruled out, Method 1 is your best bet. As for how often you should check to see if your thread is interrupted, that depends on how long it takes your computations to run and how responsive you want your program to be to a Stop command - you can add several println(System.currentTimeMillis)
statements to get an idea of the computations' timing, then add an if(Thread.interrupted()) return;
every 500 milliseconds or so to halt the run
method.
I wouldn't immediately rule out Method 2, however, as your computations don't need to have anything in common in order for you to put them in Runnables
private ArrayList<Runnable> runnables = new ArrayList<>();
runnables.add(new Runnable() {
public void run() {
// computation 1
}
})
runnables.add(new Runnable() {
public void run() {
// computation 2
}
})
// etc
public void run() {
for(Runnable runnable: runnables) {
if(Thread.interrupted()) return;
runnable.run();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With