【问题标题】:How does one stop a thread without a stop() method?如何在没有 stop() 方法的情况下停止线程?
【发布时间】:2010-07-13 16:31:06
【问题描述】:

我对 Java 线程有疑问。这是我的场景:

我有一个线程调用一个可能需要一段时间的方法。线程保持在该方法上,直到我得到结果。如果我以相同的方式向该方法发送另一个请求,现在有两个线程正在运行(前提是第一个尚未返回结果)。但我想优先考虑最后一个线程,不想从之前启动的线程中获取结果。那么在没有停止方法的情况下如何摆脱较早的线程呢?

【问题讨论】:

  • 我真的不明白你是想优先线程还是真的停止线程!?
  • 不想优先考虑这些。但是 2 避免所有以前的...

标签: java android multithreading delay


【解决方案1】:

标准的设计模式是在线程中使用一个局部变量,可以设置为停止它:

public class MyThread extends Thread {
   private volatile boolean running = true;

   public void stop() {
      running = false;
   }

   public void run() {
      while (running) {
         // do your things
      }    
   }
}

这样你可以优雅地终止线程,即不抛出InterruptedException

【讨论】:

  • 如果 things 处于循环中或可被run() 方法中断,那确实会更干净。此外,您可以将 volatile 修饰符添加到 running
  • @streetpc:谢谢,确实忘记了volatile 关键字。通常长时间运行的“事物”已经处于循环中 - 因此您可以合并循环以同时考虑 running 状态。
  • 小东西;在 Java 中它是布尔值,而不是布尔值
【解决方案2】:

最好的方法实际上取决于该方法的作用。如果它等待某些东西,interrupt 很可能会导致您处理并干净退出的 InterruptedException。如果它正在做一些忙碌的事情,它不会:

class Scratchpad {
    public static void main(String[] a) {
        Thread t = new Thread(new Runnable() {
            public void run() {doWork();}
        });
        t.start();

        try {
            Thread.sleep(50);
        } catch (InterruptedException ie) {}

        t.interrupt();
    }

    private static void doWork() {
        for ( long i = 1; i != 0; i *=5 );
    }
}

在上述情况下,唯一可行的解​​决方案确实是一个标志变量,以便在取消时尽早跳出循环,ala @inflagranti。

事件驱动架构的另一个选项是毒丸:如果您的方法正在阻塞队列中等待新项目,那么您可以拥有一个称为“毒丸”的全局常量项目,当被消耗时(出队) 你杀死线程:

try {
   while(true) {
      SomeType next = queue.take();
      if ( next == POISON_PILL ) {
          return;
      }
      consume(next);
   }
} catch //...

编辑

看起来您真正想要的是executor service。当您向执行器服务提交作业时,您会返回一个 Future,您可以使用它来跟踪结果并取消作业。

【讨论】:

  • 其实线程在等待推特的搜索结果,你认为中断会起作用吗?
【解决方案3】:

你可以interrupt一个线程,它的执行链大部分时间都会抛出一个InterruptedException(见the documentation中的特殊情况)。

【讨论】:

    【解决方案4】:

    如果你只是想减慢另一个线程而不让它退出,你可以采取一些其他的方法......

    一方面,就像退出一样,您可以有一个取消优先级的变量,当设置该变量时,每次迭代都会让您的线程休眠 100 毫秒。这将在您的其他线程搜索时有效地停止它,然后当您重新确定它的优先级时,它会恢复到全速。

    但是,这有点草率。由于您只希望一件事运行,但您希望它记住在优先级完成时处理其他事情,您可能希望将您的处理放入具有重复调用的 .process() 方法的类中。当您希望暂停处理该请求时,您只需停止对该对象调用 .process 一段时间即可。

    通过这种方式,您可以实现这些对象的堆栈,而您的线程只需执行 stack.peek().process();每次迭代,因此将一个新的、更重要的任务压入堆栈会自动停止任何先前的任务运行。

    这会导致更灵活的调度——例如,如果没有什么可做的,你可以让 process() 返回 false,此时你的调度程序可能会转到堆栈上的下一项并尝试它的进程( ) 方法,在单个线程中为您提供一些严肃的多任务处理能力,而不会过度使用您的资源(网络,我猜)

    【讨论】:

      【解决方案5】:

      Thread 有一个 setPriority(int) 方法。您可以像这样设置第一个线程的优先级:

      Thread t = new Thread(yourRunnable);
      t.start();
      t.setPriority(Thread.MIN_PRIORITY); // The range goes from 1 to 10, I think
      

      但这不会杀死你的线程。如果您只有两个线程使用您的可运行文件,那么这是一个很好的解决方案。但是如果你在循环中创建线程并且总是将最后一个线程的优先级设置为最低,你会得到很多线程。
      如果这是应用程序要做的事情,请查看ThreadPool。这不是 Java API 中的现有类。您将自己创建一个。
      ThreadPool 是另一个Thread,它以您想要的方式管理所有其他Threads。您可以设置最大运行线程数。在那个线程池中,您可以实现一个自动管理线程优先级的系统。例如:您可以让旧线程获得更高的优先级,这样您就可以正确地结束它们。

      所以,如果您知道如何使用 ThreadPool,那将会非常有趣。

      【讨论】:

        【解决方案6】:

        根据java.lang.Thread API,您应该在执行一些耗时的可取消操作时使用interrupt() 方法并检查isInterrupted() 标志。这种方法可以处理不同类型的“等待情况”:
        1. 调用interrupt()方法后,wait()、join()和sleep()方法会抛出InterruptedExcetion
        2.如果线程被java.nio.channels.Selector阻塞,它将完成选择器操作
        3. 如果您正在等待 I/O 线程将收到ClosedByInterruptException,但在这种情况下您的 I/O 设施必须实现InterruptibleChannel 接口。

        如果无法以通用方式中断此操作,您可以简单地放弃以前的线程并从新线程获取结果。您可以通过java.util.concurrent.Futurejava.util.concurrent.ExecutorService 来实现。

        考虑以下代码sn-p:

        public class RequestService<Result> {
        
        private ExecutorService executor = Executors.newFixedThreadPool(3);
        
        private Future<Result> result;
        
          public Future<Result> doRequest(){
            if(result !=null){
                result.cancel(true);
            }
            result = executor.submit(new Callable<Result>() {
                public Result call() throws Exception {
                    // do your long-running service call here
                }
            });
            return result;
          }
        
        }
        

        Future 对象在这里表示服务调用的结果。如果您再次调用doRequest 方法,它会尝试取消先前的任务,然后尝试提交新请求。只要线程池包含多个线程,您就不必等到先前的请求被取消。新请求立即提交,方法返回一个新的请求结果。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-02-13
          • 2021-12-17
          • 2022-07-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多