0

i'm trying to stop a specific Thread that implement Runnable.

I already try a few things that i found while googling it, but still can't find the best answer.

I have two Class.

1st is Timer. This class will create a countdown. It will send a result if countdown == 0 to another class.

public class Timer implements Runnable {

public Timer(int countdown, Room room, String cmdName) {
    this.room = room;
    this.countdown = countdown;
    this.cmdName = cmdName;
}

public void setResultThread(IResultThread resultThread) {
    this.resultThread = resultThread;
}

@Override
public void run() {

    for (int i = countdown; i >= 0; i--) {

        countdown--;
        if (i == 0) {
            resultThread.setResult(true);
        }

        try {
            TimeUnit.MILLISECONDS.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
}

2nd is User. I want to stop the Thread from this class.

    Thread thread;
    String threadName = "player-" + user.getName();
    thread = utils.getThreadByName(threadName);

    if (thread != null) {
        thread.stop(); // i used stop before, but i read its not a good way
        thread.interrupt();
    }

    if (nextTurn == 4) {
        // do something
    } else {

        int countDown = 10
        //getting player name

        if (playerName != null) {
            String newThreadName = "player-" + playerName;

            Timer timer = new Timer(countDown, room, Send.PLAYER_TIMER.toString());
            thread = new Thread(timer, newThreadName);
            timer.setResultThread(resultThread);
            thread.start();
        }
    }

I'm not sure if there's something wrong with my code or something. I tried catch the exception, but the Thread countdown is still running. Its seems like its interrupt while the Thread is sleep. I ever tried with volatile boolean, but I don't think is a good way to approach in this case and of course its not working either.

I used thread.stop() before, but its not a good idea. This is the 1st time I'm using Thread, and its still a little bit confused for me.

I will appreciate any suggestion and answer. Thanks alot.


ANSWER

I put an answer just in case there's someone have a same problem with me.

@Override
public void run() {

    try {
        while (countdown >= 0) {

            TimeUnit.MILLISECONDS.sleep(1000);
            countdown--;
            if (countdown == 0) {
                resultThread.setResult(true);
            }
        }
    } catch (InterruptedException e) {
        countdown = 0;
        Thread.currentThread().interrupt();
        //e.printStackTrace();
    }
}
Matthew
  • 3
  • 5
  • Catch the interruption outside your for loop. As it stands, you just go on to the next loop iteration when the sleep is interrupted. To put this another way: your try/catch should be around the for loop, not the for loop around the try/catch. – Andy Turner Jun 15 '17 at 10:05
  • @AndyTurner thanks for your suggestion. I tried set the `i=0` and its work. But is it ok if i remove `e.printStackTrace();`. I don't get it about for the loop around the try/catch. Do you mean i should put the for loop inside try/catch? – Matthew Jun 15 '17 at 10:24
  • yes, for loop inside try/catch. You should *nearly always* call `Thread.currentThread().interrupt();` when you catch an `InterruptedException`, to preserve the fact that the thread was interrupted for callers who may need to know. (Don't worry, it doesn't interrupt the thread again, it just sets a boolean flag). Whether you use `e.printStackTrace()` as well is entirely up to you; it serves little practical benefit. – Andy Turner Jun 15 '17 at 10:28
  • @AndyTurner thank you, maybe you can change your comment into an answer. So I can accept it :D – Matthew Jun 15 '17 at 10:33

4 Answers4

0

The preferred way, IMO, to stop your Timer is to have eg. a cancel method

public class Timer implements Runnable {
  ...
  public void cancel() {
    countdown = 0;
  }
  ...
}

Then replace thread.stop() with timer.cancel(), obviously you would need a reference to timer or have Timer extend Thread instead of implementing Runnable.

And as Andy said, in your run() method it should be

run() {
  ...
  try {
    for () {
      ...
    }
  } catch (...
}
TedTrippin
  • 3,525
  • 5
  • 28
  • 46
  • Thank you for your answer. But, Andy answer is more simple and not to much effort. :D – Matthew Jun 15 '17 at 10:34
  • Fair enough if you'd rather go with a "simple but dangerous" approach, that's your decision ;) If you're just testing something then fair enough. Like you said, `stop()` is a bad idea and its use massively discouraged. Its well documented why if you care to google it. – TedTrippin Jun 15 '17 at 10:45
  • @TedTrippin this wouldn't be thread safe as presented. You'd either have to synchronize all reads of `countdown`, or make it `volatile`, or use `AtomicInteger`. – Andy Turner Jun 15 '17 at 11:07
  • Plus, changing `countdown` to zero only has an effect if it occurs before the loop executes: `countdown` is only read in the `ForInit`; after that, it is unused, so this wouldn't do anything. – Andy Turner Jun 15 '17 at 11:31
  • @AndyTurner I can't see any thread safety issues, such as you've pointed out, but you're right about the countdown check. I should have mentioned replacing the for loop with a `while (countdown > 0)` or some such. – TedTrippin Jun 15 '17 at 14:09
  • 1
    @Ted it's a classic visibility issue: JMM does not guarantee that updates from the thread calling `cancel()` will be visible in threads executing `run()`. You have to use something to ensure that the write in `cancel()` *happens-before* the read in `run()`. – Andy Turner Jun 15 '17 at 15:59
  • @AndyTurner in this case the change in value would eventually be seen by the other thread. There's a slim chance the timer may have to wait another second, but in this case that's not a problem. As your good knowledge of java is evident I would have hoped you would be advocating against the use of stop() :) – TedTrippin Jun 15 '17 at 16:42
  • 1
    I am not advocating the use of stop; I thought it was obvious, but I've put it explicitly in my answer. I am advocating the correct use of cooperative interruption. And no, there is no guarantee that the other thread will "eventually" see the updated value. – Andy Turner Jun 15 '17 at 16:48
  • Hi guys, thank for your discussion, but I am not use `.stop();` anymore since I write this question. And I change the for loop with while loop. I really appreciate both of your answer. It teach me alot. :). Since I have to change about 3 - 4 class if I extend `Thread`, and I already extend other class on the other class that I called `Timer`. That's the reason why I preferred implement `Runnable`. – Matthew Jun 16 '17 at 03:37
  • @Matthew you're welcome. Not saying you would but please don't ever choose the "less change" option cus its easier. Strive for best :D @AndyTurner I'm not doubting you, merely intrigued, but would you be able to explain or provide a link as to why it wouldn't eventually see the change in this scenario? I get the scenario `int x = 1; while (x==1)...` might get optimised but i don't see that here. – TedTrippin Jun 16 '17 at 08:28
0

There is no need to call Thread.stop().

The reason why your code doesn't currently stop when you interrupt the thread is that the handling of the interruption occurs inside the loop:

for (int i = countdown; i >= 0; i--) {
  // ...
  try {
    // Sleep
  } catch (InterruptedException e) {
  }
}

When the thread is interrupted during (or before) the sleep, the interruption is "swallowed": you handle it, so the loop just continues on to the next iteration, if any.

There's a Robin Williams skit about British police, who, owing to their lack of guns, have to say "Stop! Or.... I'll say stop again!". If you kept calling interrupt, eventually your thread would finish, because eventually the loop would execute the appropriate number of times. But this would be really messy, and is quite unnecessary.

Reverse the order of the loop and the try/catch:

try {
  for (int i = countdown; i >= 0; i--) {
    // ...

    // Sleep
  }
} catch (InterruptedException e) {
}

Now, when the interruption occurs, execution moves to after the loop, so no more loop iterations occur.

(Of course, you could leave your code as it is, and just return from the catch; but multiple return points can make the code harder to reason about)


You should also consider re-interrupting the current thread when you catch the InterruptedException:

// ...
} catch (InterruptedException e) {
  Thread.currentThread().interrupt();
}

This sets the interrupted flag on the current thread (confusingly, this is orthogonal to InterruptedException), allowing any code calling the Runnable to know that the thread was interrupted.

It's not strictly necessary to do this: if you know that the code is at the top of a thread's call stack (like here: there is no "caller" to be made aware of interruption). But re-interrupting the thread is nearly always the right thing to do (*), and doesn't actually hurt at all in this case.

(*) An example of when you wouldn't is if you are rethrowing the InterruptedException, or if you are writing threading framework code, and you know for sure it is the right thing not to.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
0

The correct way to stop a thread from another class is to interrupt the running thread. Then within the thread you must detect the interruption and then end the thread execution.

don't call thread.stop, also there is no need to use volatile variables

for example

public class StoppingThreadFromOtherClassExample {

    public static void main(String[] args) throws InterruptedException {
        StoppingThreadFromOtherClassExample stoppingThreadFromOtherClassExample = new StoppingThreadFromOtherClassExample();
        stoppingThreadFromOtherClassExample.doTheWork();
    }

    private void doTheWork() throws InterruptedException {

        Thread myThread = new Thread(new MyClassA());
        myThread.start();

        Thread.sleep(1000);
        myThread.interrupt();

        myThread.join();

    }
}

class MyClassA implements Runnable {

    int counter = 0;

    @Override
    public void run() {
        while (true) {
            counter++;
            System.out.println(counter);
            if (Thread.interrupted()) {
                System.out.println("this thread was interrupted. it will stop now!!");
                break;
            }
        }
    }
}
Jose Zevallos
  • 685
  • 4
  • 3
-2

You can give each thread a name and whenever you want to close the thread just iterate through the collection looking up for the exact name of the thread and call the stop.

To get the list of running thread

get an iterable set:

Set threadSet = Thread.getAllStackTraces().keySet();