代码之家  ›  专栏  ›  技术社区  ›  royalghost

当线程在某个循环中执行长任务时,如何中断它?

  •  1
  • royalghost  · 技术社区  · 16 年前

    可能重复: How do you kill a thread in 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循环接收到中断信号时,我想基本上停止循环(请注意,我无法将Thread.interputed()逻辑放入for循环中,因为它来自客户端。)我有另一个类使用Executor来执行此任务。

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

    }

    任务2是:

    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循环)。我需要社会各界的帮助来解决这个问题。谢谢。

    9 回复  |  直到 8 年前
        1
  •  3
  •   Stephen C    16 年前

    唯一的防爆解决方案是以隔离方式运行客户端提供的代码。隔离实际上是一个私有虚拟机,Java应用程序可以创建、管理和通信。尤其是,父应用程序可以安全地终止隔离及其所有线程。

    参考: JSR-000121 Application Isolation API Specification - Final Release

    问题是找到一个支持隔离的JVM。

        2
  •  2
  •   Gregory Mostizky    16 年前

    重新阅读你的问题,意识到你对任务代码没有任何控制权。

    task1不中断的原因是interrupt()方法实际上不会中断任何东西,它只会在线程等待某个锁或睡眠时导致线程中断,否则除了设置状态之外,它实际上不会执行任何操作。

    唯一可以杀死task1的方法是使用Thread.stop()方法。 但要小心,因为它可能会非常危险,使您的系统不稳定。 查看更多 here .

        3
  •  2
  •   LPL user462990    13 年前

    您可以使用“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 ); // e.g. "task1"
       }
    
       public void AbortAllTasks() {
           terminate.set(true);
       }
    
       public boolean abort() {
           return terminate.get();
       }
    } 
    

    您的任务可以将控制器(“this”指针)作为构造参数,并检查 run() . 这允许您通过调用 TaskController.AbortAllTasks() . 对于您无法控制的任务,您可以将自己的中断逻辑放入 TaskController.run() .

        4
  •  1
  •   sjlee    16 年前

    打扰真的是 合作的 取消机制,不是抢先机制。完成一个优雅的取消任务需要任务的配合。如果任务是不可中断的(即,不通过检查中断状态或响应中断异常来中止其操作),则取消该任务的方法不多。传递中断是微不足道的;它实际上是任务对它的响应方式。

    一种可能的解决方案可能是通过诸如动态检测之类的方法将代码注入到任务中。但是,即使这样也不一定,如果任务使用while循环,也需要在while循环中注入代码。

        5
  •  1
  •   weilin8    16 年前

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

        6
  •  0
  •   SergeySergey    16 年前

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

                while(true){
                        if(Thread.interrupted()){
                                return;
                        }
                        for(int i=0; i<Integer.MAX_VALUE; i++){
                             if(Thread.interrupted()){
                                  return;
                             }
    
                             System.out.println("I am task 1 " + i);
                        }
    
                }
    
        7
  •  0
  •   Harold L    16 年前

    如果其他人在工作线程中提供代码(作为jar?)他们会把你的代码作为他们工作的一部分吗?您可以检查您编写的函数中是否有中断,并抛出一个未经检查的异常,我们希望该异常将终止线程。

    // 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 ...
    }
    

    小心警告编写代码的开发人员:请参见 this article describing some of the problems that can arise .

    因此,简而言之,如果你不写代码,最好是让任何人检查线程。中断(),这样他们就可以干净地退出。

        8
  •  0
  •   Gregory Mostizky    16 年前

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

    例如:

    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
            }
        }
    }
    
        9
  •  0
  •   Ofek Ron    13 年前

    如果不在for语句本身中进行更改,则无法进行此操作。由于Sun取消了立即挂起线程的方法。他们有他们的理由。( see here )唯一的方法是在循环中使用布尔值,或者,如果线程在for循环中休眠/等待,则可以捕获Thread.interuppt()抛出的InterruptedException