当一些循环执行长任务时如何中断线程?

可能重复: 你如何杀死Java中的线程?


我需要通过向线程发送中断信号来停止执行一些大任务。 我正在使用java.util.concurrent。*中的大多数API。 我的任务是发送到线程并执行。 此任务来自客户端,因此我无法控制该代码。

任务类似于:

public class Task1 extends Thread { public void run() { while(true){ if(Thread.interrupted()){ return; } for(int i=0; i<Integer.MAX_VALUE; i++){ System.out.println("I am task 1 " + i); } } } }; 

我希望在接收到中断信号时基本上停止循环for-loop(请注意我不能将Thread.interrputed()逻辑放在for循环中,因为它来自客户端。)我有另一个使用它的类执行程序执行此任务。

 public class ConcurrentTest { public static void main(String[] args) { // TODO Auto-generated method stub ConcurrentTest test = new ConcurrentTest(); ExecutorService executorService = Executors.newFixedThreadPool(2); Task1 task1 = new Task1(); Future runningTask1 = executorService.submit(task1); Task2 task2 = new Task2(); Future runningTask2 = executorService.submit(task2); //Stop First task after 5 second try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } runningTask1.cancel(Boolean.TRUE); System.out.println("runningTask1.isDone is " + runningTask1.isDone()); System.out.println("runningTask1.isCancelled is " + runningTask1.isCancelled()); //Stop Second task after 10 second try{ TimeUnit.SECONDS.sleep(3); }catch(InterruptedException e){ e.printStackTrace(); } runningTask2.cancel(Boolean.TRUE); System.out.println("runningTask2.isDone is " + runningTask2.isDone()); System.out.println("runningTask2.isCancelled is " + runningTask2.isCancelled()); } 

}

Task2是:

 public class Task2 extends Thread{ public void run() { while(true){ if(Thread.interrupted()){ return; } System.out.println("I am task 2"); } } }; 

Task2是互相关联的,但Task1永远不会被中断并继续在for循环中执行。 我不能在客户端代码中添加任何逻辑(类似于for循环)。 我需要SO社区的帮助来解决这个问题。 谢谢。

唯一防弹解决方案是在Isolate中运行客户端提供的代码。 隔离本质上是Java应用程序可以创建,管理和通信的私有虚拟机。 特别是,父应用程序可以安全地杀死隔离及其所有线程。

参考: JSR-000121应用程序隔离API规范 – 最终版本

问题是找到支持Isolates的JVM。

重新阅读您的问题并意识到您无法控制任务代码。

task1不会中断的原因是interrupt()方法实际上没有中断任何东西,只会在线程等待一些锁定或hibernate时才会导致线程中断,否则除了设置之外它实际上什么都不做地位。

杀死task1的唯一方法是使用Thread.stop()方法。 但请注意,它可能非常危险,并使您的系统不稳定。 在这里查看更多。

您可以使用“停止”标志和包装类来终止您自己的任务以及客户端为您提供的任务。

 public class MyTask implements Runnable { private TaskController taskControl; @Override public void run() { if(taskControl.abort()) { return; } // Task logic ... } } // Construct on main thread, call "run()" on a dedicated background thread. public class TaskController implements Runnable { private AtomicBoolean terminate = new AtomicBoolean(false); private ThreadPoolExecutor yourTaskThreadPool; private ThreadPoolExecutor otherTaskThreadPool; @Override public void run() { // Your task construction, dispatch logic. Use your own thread pool // Run the tasks you don't control in THEIR own pool if(terminate.get()) { otherTaskThreadPool.shutdown(); } otherTaskThreadPool.execute( clientsTask ); // eg "task1" } public void AbortAllTasks() { terminate.set(true); } public boolean abort() { return terminate.get(); } } 

您的任务可以将控制器(“this”指针)作为构造参数,并检查其run()的terminate标志。 这允许您通过调用TaskController.AbortAllTasks()来异步杀死所有这些。 对于您无法控制的任务,您可以将自己的中断逻辑放在TaskController.run()

中断实际上是一种合作取消机制,而不是先发制人机制。 它需要从任务的合作做出优雅的取消。 如果任务是不可中断的(即不通过检查中断状态或响应InterruptedException而中止其操作),则无法取消任务。 提供中断是微不足道的; 这真的是任务如何响应它。

一种可能的解决方案可能是通过动态检测等方法将代码注入任务。 但是,即使这不是一个肯定的赌注,如果任务使用while循环,它将需要在while循环中注入代码。

我会提供一种机制,允许客户端代码协同处理停止信号。 如果客户端代码处理信号,那么您的应用程序和客户端都同意它可以及时停止。 否则,当代码返回到interrupted()检查时,您将中断该线程。 我的观点是你应该要求客户代码合作而不是盲目地打断它。

在while循环和for循环中检查两次中断。

  while(true){ if(Thread.interrupted()){ return; } for(int i=0; i 

如果其他人在工作线程中提供代码(作为jar?),他们会根据工作的一部分调用您的代码吗? 您可以将检查中断的函数放入他们调用的函数中,然后抛出一个未经检查的exception,我们希望它将终止该线程。

 // Called from the worker thread code that someone else writes, // hopefully often so that we notice when we're interrupted. public void doSomething() { if (Thread.interrupted()) { throw new MyRuntimeException(); } // do what this method is meant to do ... } 

小心警告编写该代码的开发人员:请参阅本文,描述可能出现的一些问题 。

因此,简而言之,如果您不编写该代码,最好尝试让任何人检查Thread.interrupted()以便它们可以干净地退出。

停止线程的一种简单方法是为每个线程定义一些停止标志 ,然后定期检查该标志。

例如:

 class MyTask implements Runnable { private AtomicBoolean stopflag = new AtomicBoolean(); public void stop() { stopflag.set(true); } public void run() { while(true) { if(!stopflag.get()) /// do some work else return; // get out } } } 

如果不在for语句本身中进行更改,则无法进行此操作。 因为Sun贬低了立即线程暂停的方法。 他们有他们的理由。 ( 参见这里 )唯一的方法是在循环中使用布尔值,或者,如果线程在for循环内等待,则可以捕获由Thread.interuppt()引发的InterruptedException