Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java - terminating a method within a Thread

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!

like image 959
CodeGuy Avatar asked Feb 12 '14 03:02

CodeGuy


2 Answers

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.

enter image description here

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!

like image 53
Jorge Palacio Avatar answered Oct 08 '22 22:10

Jorge Palacio


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();
  }
}
like image 24
Zim-Zam O'Pootertoot Avatar answered Oct 08 '22 21:10

Zim-Zam O'Pootertoot