如何在Java中停止Thread

时间:2013-07-05 04:54:31

标签: java multithreading

我一直在学习线程,是的...我是一个noobie。我有一个问题,我不知道如何停止线程,因为方法.spot()已被弃用。我想知道如何安全地停止一个线程。我这样做了:

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.

所以我正在为自己做一个迷你游戏,而我的方法体是一个天文台,向我展示了我需要多长时间来解决这个难题。这笔交易是,当我开始新游戏时,我调用重置秒,分钟和小时值以及changeBo1()的方法 所以时钟停止了。然后,当我在屏幕上第一次点击时,时钟开始。然后,当我改变游戏时。但线程没有读取布尔值的变化,它重新启动到0:00:00,没有我点击屏幕,一直到0:00:01 --- (这不会发生在第一个游戏中,因为变量是需要的,当我点击“新游戏”时,当变量需要改变时)。

如何让线程读取boolean bo1的变化?

4 个答案:

答案 0 :(得分:6)

首先,将volatile关键字添加到bo1

private volatile boolean bo1;

volatile keyword保证当任何线程写入变量时,来自任何其他线程的任何后续读取将读取更新的值。缺少关键字可能会导致一些不合理的优化,基于假设变量仅在每个线程的基础上使用。

此外,您的函数changeBo1()可以简化为:

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

编辑1:另一种方法是做这个:

tr1.interrupt();

这对你有两件事:

  1. 如果tr1内的操作调用Object.wait();,则中断标志将释放线程上的块,因此可以关闭。
  2. 您不再需要布尔值来跟踪线程何时应该中断;您可以在run()

    中使用以下代码段
    while(!isInterrupted()){
        ...
    }
    

答案 1 :(得分:6)

制作bo1 volatile

private volatile boolean bo1;

这是因为,有时一个线程可以在另一个线程更改该值后读取过时值。对此的简单解释是每个线程都有自己的内存副本,两个线程共享,有时私有副本可能不同步。

bo1 是通常所说的sentinel变量。只是一种说法变量控制循环的奇特方式。

请记住,两个线程不同步,因此它们可以处于任何状态。你必须小心控制这种状态。仅仅因为你将bo1设置为false并不意味着使用它作为 sentinel变量的线程实际上已经完成执行,因为它可能尚未处理此更改并退出循环。< / p>

您需要join threads以确保执行完成。如果Thread1在Thread2上调用join,则Thread1将被阻塞,直到Thread2完成执行。 (不会阻止在自身上调用join的线程。)

这是使用上述技术的重写。

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.

如果您处于需要循环处理数据的情况(例如解析流协议,或只是处理某些项目列表),那么遵循这样的模式可能很有用:

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();
        }
    }
}

这会调出notifywait个功能。在对象上调用Wait将阻塞线程,直到另一个线程在同一对象上调用notifynotifyAll。这可以用于指示数据可用,或者更一般地,已经发生了一些值得注意的事件。这是必要的,因为不等待将导致线程尽可能快地运行,消耗大量CPU。

答案 2 :(得分:2)

首先停止线程需要一个Conditional变量。在你的情况下 它是

private boolean bo1; // should declare it volatile

第二个非常重要的一点,你需要中断相应的线程 你要停下来。因为你的线程可能就是这种情况 等待(wait())并且不会检查条件变量因此不会停止。

即你需要致电

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

永远记住一个线程只有在正常运行方法/正常运行方法时才会死亡/停止。

答案 3 :(得分:0)

方法1

正如其他人回答的那样bo1应该使用volatile关键字进行修改。并且可以使用synchronized关键字修改那些访问块。

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;
}

方法2

有一种方便的线程中断方法。检查Thread#isInterrupted()

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();
    }
}