4

i started to read about how to stop, interrupt, suspend and resume safely a java thread, i found on oracle documentation the following solutions :

1- How to stop a thread safely :

private volatile Thread blinker;

public void stop() {
    blinker = null;
}

public void run() {
    Thread thisThread = Thread.currentThread();
    while (blinker == thisThread) {
        try {
            Thread.sleep(interval);
        } catch (InterruptedException e){
        }
        repaint();
    }
}

- To stop a thread i can use a boolean variable instead of volatile Thread, but why Oracle insists on affecting null to the started thread? is there any secret (e.g liberating resources allocated using finalizer) behind doing it like this?

2- How to interrupt a thread which waits for long time :

public void stop() {
    Thread moribund = waiter;
    waiter = null;
    moribund.interrupt();
}

- why should i create new variable moribund and not using directly waiter.interrupt()?

3- How to suspend and resume a thread :

private volatile boolean threadSuspended;

public void run() {
    while (true) {
        try {
            Thread.sleep(interval);

            if (threadSuspended) {
                synchronized(this) {
                    while (threadSuspended)
                        wait();
                }
            }
        } catch (InterruptedException e){
        }
        repaint();
    }
}

public synchronized void mousePressed(MouseEvent e) {
    e.consume();

    threadSuspended = !threadSuspended;

    if (!threadSuspended)
        notify();
}

- Why inside run method they added the loop while (threadSuspended) because i don't understand what the purpose of adding it, and my code can be compiled and run correctly without it (with same output results).

Source link http://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html

Naruto Biju Mode
  • 2,011
  • 3
  • 15
  • 28

1 Answers1

3

1. Using a local Thread variable prevents other threads invoking the run() method on your object. Only the thread represented by this object instance can use the run() method. It is generally bad practice to invoke the run() method of a Thread manually from a different Thread, but certainly possible.

2. This point needs to be explained in the context of point 1. This part also considers the case when interval is very long, and the thread needs to be stopped ASAP.
You certainly need to nullify the reference because otherwise the code in part 1 will just continue looping. But consider what may happen if you simplify the stop method to:

public void stop() {
    waiter.interrupt();
    waiter = null;    
}

Since this is executed from another thread, it can be intertwined with the run() method in any way. For example, threadA calls stop() to stop threadB which is in run():

  1. threadB: sleep(interval)
  2. threadA: waiter.interrupt()
  3. threadB: caught InterruptedException
  4. threadB: call to repaint
  5. threadB: enter next while cycle
  6. threadB: enter sleep(interval)
  7. threadA: waiter == null

In this case, instead of immediately stopping, threadB does another cycle of sleeping, which fails the set task of stop a thread that waits for long periods. In the given implementation you first nullify, then interrupt, which prevents this kind of behaviour.

3. In short: Because another thread may have notified your code, without setting the correct flag. The general contract of notify() is that it is harmless to call (but a useless call will obviously consume some resources). All threads are supposed to be able to cope with spurious wake-ups.

Ordous
  • 3,844
  • 15
  • 25
  • Thanks really for your great work, i understood the second point, for the first point i already meet a problem which i clarified in this thread http://stackoverflow.com/questions/23788838/safe-way-to-stop-java-thread , i couldn't found a great solution using inner thread but it works great with boolean flag, for the third point i still don't understand what is the benefice for adding the loop `while (threadSuspended)` because if i delete it my code works perfectly without it. – Naruto Biju Mode May 22 '14 at 00:16
  • 1
    @NarutoBijuMode See, this is why you have to put your sources when asking a question. The answer given, although correct, does not explain the real problem - your incorrect implementation of the safety mechanism. The reference should be created in the constructor, not in a make-shift method. As for point 3 - it's called best practice. Any method using `wait()` should be looping to not die when woken up by someone else mistakenly – Ordous May 22 '14 at 11:19
  • Thanks your are really genius, for the first point i failed to implement that solution using inner thread member created inside outer constructor class, so i think i will use just simple volatile boolean flag for my futures applications, for the others points you explained them very well, thanks so much. – Naruto Biju Mode May 22 '14 at 13:34
  • @Ordous How does the first given implementation for 2 "prevents this kind of behaviour"? Won't it simply interrupt the thread at the same point that it would've with the waiter thread object then continue on like nothing has happened? (Assuming it doesn't check to see if it has been interrupted, which it doesn't in 1) – Everlight Oct 12 '15 at 18:15
  • @Everlight Bah, necroposter! Not sure what you mean by "same place" in the context of two threads running - some *configurations* are impossible in one case, some others in the other. In this case the configuration we want to be impossible is the one where after the `interrupt` call a new (last) cycle is started. This is achieved by nullifying the thread (since that's the condition of the loop) as a precondition for the `interrupt`. – Ordous Oct 12 '15 at 19:15
  • @Ordous I try =) When I say "same point" I mean: `Thread.sleep(interval);` in the original question's first code block. Grrr, but, I'm still confused, you set `waiter` to null, then interrupt `moribund`, then wouldn't moribund be interrupted at `Thread.sleep(interval);`. Which would be the configuration you are trying to make impossible, no? – Everlight Oct 12 '15 at 21:46
  • 1
    @Everlight That's the start of it, yes. In fact, it's pretty much assumed that the `interrupt` will wake up the thread at the `sleep`. It's what happens next is in question. In one case - the thread object is already nullified, so the while loop is guaranteed to not execute again. In the other case, one thread is calling repaint and then checking the loop, while the other is setting the waiter to null. That is the race condition that is being avoided, and depending on what happens first - there may or may not be another iteration of the loop. – Ordous Oct 13 '15 at 01:29
  • Ahhh AHHHH AHHHHHH!!! AHAH! I get it now =) When the Thread is set to null, then interrupted, the thread is now de-referenced and eligible for garabage collection. If its interrupted then set to null it has a race condition that could let it keep going forever and ever (ish) =) – Everlight Oct 14 '15 at 17:34
  • @Everlight Well almost - it won't go on forever, it will go on until it has been dereferenced *and then* checked in the loop. In most cases this will happen after at most a single `sleep`. By nullifying and then interrupting we force a check right after the dereference , without any delay or `sleep`s in between. – Ordous Oct 14 '15 at 17:40