简体   繁体   中英

How to stop a Thread in Java

I've been learning about Threads, yeha... I'm a noobie. I have one problem, i don't know how to stop a Thread because the method .spot() was deprecated. I want to know how to safely stop a thread. I did this:

public class Principal{

    private boolean bo1;
    private Thread tr1;


    public Principal(){

        bo1 = true;
        tr1 = new Thread(){
            public void run(){
                while(bo1){
                    //What the Thread does goes here, its a chronometer.
                }          
            }
        };  // The thread body stops here.
    }   // The constructor body stops here. 

    public void changeBo1(){
        if(bo1 == true){
            bo1 = false;
        }

        else{
            bo1 = true;
        }    
    } //Change bo1 ends here.
} //Class ends here.

So i'm making a mini-game for myself, and I the body of the method is a chronometer that shows me how long i'm taking to solve the puzzle. The deal is, when I start a new game, i call the method that resets the value of the seconds, minutes and hours and the changeBo1() so the clock stops. Then when I make the first click in the screen, the clock begins.Then, when I change the game. But the thread is not reading the change of value of the boolean, it restarts to 0:00:00 and, without me clicking the screen, keeps going to 0:00:01--- (This doesn´t happen with the first game, since the variables are the ones needed, it happens when i click "new games", when the variables need to change).

How do I need to allow the thread to read the change of the boolean bo1?

First, add the volatile keyword to bo1 :

private volatile boolean bo1;

The volatile keyword guarantees that when any thread writes to the variable, any subsequent reads from any other thread will read the updated value. The lack of the keyword may cause some undesirable optimizations, based on the assumption the variable is only used on a per thread basis.

Also your function changeBo1() can be simplified to:

public void changeBo1() {
    bo1 = !bo1;
}

EDIT 1: Another way to do this is instead do this:

tr1.interrupt();

Which does two things for you:

  1. If an operation inside tr1 calls Object.wait(); , the interrupt flag will release the block on the thread, so it can be shutdown.
  2. You would no longer need a boolean to track when the thread should interrupt; you can use the following snippet inside run() :

     while(!isInterrupted()){ ... } 

Make bo1 volatile :

private volatile boolean bo1;

This is because, sometimes, one thread can read a stale value after another thread has changed that value. The simple explanation for this is each thread has its own copy of memory that both threads share, and sometimes that private copy can be out of sync.

bo1 is what's commonly referred to as a sentinel variable. Just a fancy way of saying the variable controls a loop.

Remember, the two threads aren't synchronized, so they could be in any state. You must carefully control that state. Just because you set bo1 to false doesn't mean the thread using that as a sentinel variable is actually finished executing, as it may not have processed this change and exited the loop yet.

You'll need to join threads to ensure execution completion. If Thread1 calls join on Thread2, Thread1 will be blocked until Thread2 is finished executing. (A thread calling join on itself will not be blocked.)

Here's a rewrite, using the techniques mentioned above.

public class Principal{

private boolean bo1;
private Thread tr1;


public Principal(){

    bo1 = true;
    tr1 = new Thread(){
        public void run(){
            while(bo1){
                //What the Thread does goes here, its a chronometer.
            }          
        }
    };  // The thread body stops here.
}   // The constructor body stops here. 
public void stopThread(){
    changeBo1();
    tr1.join();
}
public void changeBo1(){
        bo1 = !bo1;
} //Change bo1 ends here.
} //Class ends here.

If you are ever in a situation where you need to loop to process data (such as parsing a streaming protocol, or just processing some list of items) it may be useful to follow a pattern like so:

public class ProcessRunnable implements Runnable{
    private final Queue<Item> queue = new LinkedList<Item>();
    public void run(){
        while(active){
            synchronized(queue){
                while(queue.size()>0){
                    Item item = queue.remove();
                    //process item
                }
                queue.wait();
            }
        }
    }
    public void add(Item item){
        synchronized(queue){
            this.queue.add(item);
            this.queue.notifyAll();
        }
    }
}

This brings up the notify and wait functions. Calling Wait on an object will block a thread until another thread calls notify or notifyAll on the same object. This can be used to indicate data is available, or more generally, some notable event has occurred. This is necessary because not waiting will cause a thread to run as fast as possible, consuming much CPU.

For stopping a thread first you need a Conditional variable. In your case it is

private boolean bo1; // should declare it volatile

second very important point you need to interrupt the corresponding thread which you want to stop. Because it may be the case that your thread is waiting (wait()) and will not check the condition variable and hence will not stop.

ie you need to call

tr1.interrupt(); // interrupts the  thread and brings out of wait state

Always remember a thread only dies/stop when its complete is run method normally/or through some exception.

Approach 1

As others answered the bo1 should be modified with volatile keyword. And those accessing block might be modified with synchronized keyword.

class MyThread extends Thread {

    public void run() {
        while (true) {
            synchronized(this) {
                if (stop) {
                    break;
                }
            }
            // do your job
        }
    }

    public synchronized void setStop() {
        stop = true;
    }

    private volatile stop = false;
}

Approach 2

There is a handy method for thread interruption. Check Thread#isInterrupted() out.

class MyThread extends Thread {

    public void run() {
        while (!isInterrupted()) {
            // do your job
        }
    }

    public static void main(final String[] args) throws InterruptedException {
        final Thread thread = new MyThread();
        thread.start();
        Thread.currentThread().sleep(10000L);
        thread.interrupt();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM