如何在Java中停止Thread

我一直在学习线程,是的…我是一个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的变化?

使bo1 易变 :

 private volatile boolean bo1; 

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

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

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

您需要连接线程以确保执行完成。 如果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 queue = new LinkedList(); 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(); } } } 

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

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

 private volatile boolean bo1; 

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

您的函数changeBo1()也可以简化为:

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

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

 tr1.interrupt(); 

这对你有两件事:

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

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

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

 private boolean bo1; // should declare it volatile 

第二个非常重要的一点,你需要中断你想要停止的相应线程。 因为您的线程可能正在等待(wait())并且不会检查条件变量,因此不会停止。

即你需要打电话

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

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

方法1

正如其他人所回答的那样,应该使用volatile关键字修改bo1 。 那些访问块可能会使用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() 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(); } }