【问题标题】:Java : Calling interruptible methods from the codeJava:从代码中调用可中断的方法
【发布时间】:2011-10-25 06:45:49
【问题描述】:

我正在阅读Java并发实践的第7章。

在谈到没有自己的取消策略但调用可以被中断的方法的方法的部分中,本书有以下内容要说。

不支持取消但仍调用可中断的活动 阻塞方法必须循环调用它们,当 检测到中断。在这种情况下,他们应该保存 本地中断状态并在返回前恢复, 而不是在捕获 InterruptedException 后立即进行。

我还没有完全理解这一点。

这是否意味着,如果我在我的方法中调用Thread.sleep,我将不得不循环调用它或其他什么?

谁能解释为什么要这样做?

【问题讨论】:

标签: java multithreading concurrency interrupted-exception


【解决方案1】:

我在我的方法中调用 Thread.sleep,我将不得不在循环中调用它或其他什么?

Thread.sleep() 将在当前线程(被另一个线程)中断时抛出InterruptedException。如何应对是你的选择。如果你想睡觉,不管是否有人试图打断你,那么是的,你必须在 try-catch 块周围建立某种循环。可能你应该使用时钟(例如System.nanoTime())来检查在抛出异常之前你睡了多长时间,然后继续睡剩下的时间等等。

请注意,InterruptedException 仅在另一个线程通过调用(current)Thread.interrupt() 中断当前线程时才被抛出。它不会自行发生,因此通常您不需要围绕 sleep 或类似的东西构建任何循环。通常线程被中断只是有充分的理由(例如应用程序关闭),所以你可能想要支持取消/中断,除非没有特殊的理由不这样做。例如,“特殊原因”可能是写入 I/O 设备并尝试保证将写入所有数据,而不管取消尝试如何。

【讨论】:

    【解决方案2】:

    先解释一下:

    线程的中断状态基本上是一个布尔标志,由interrupt()设置为“true”。 该标志的当前状态可以使用Thread.currentThread().isInterrupted()读取

    如果可中断操作(如Object.wait()Thread.sleep())发现设置了中断标志 它会抛出一个InterruptedException,同时clear(设置为“false”)标志,看起来像这样:

    if ( Thread.interrupted() ) { throw new InterruptedException(); }
    

    注意并记住Thread.interrupted() 隐式清除中断标志! 这意味着,当你的 catch( InterruptedException ie) {...} 被执行时, 线程本身不再知道它被中断了。

    话虽如此,让我们看两个例子:

    首先是一个支持取消的任务示例。 在这里,我们并不关心任务在中止之前进行了多长时间:

      public void run() {
    
        int x = 0;
    
        try {
    
          while (x < 10) {
            Thread.sleep(1000); // Some interruptible operation
            x++;
          }
    
          System.out.println("x = " + x);
    
        } catch (InterruptedException ie) {
    
          System.out.println("Interrupted: x = " + x);
    
          // We know we've been interrupted. 
          // Let the caller know it, too:
          Thread.currentThread().interrupt();
        }
    
      }
    

    此代码尝试从 0 到 10 计数 x。如果没有中断,它将完成并输出“x = 10”。 但是,如果线程在两者之间被中断,InterruptedException 将被抛出,中止正在进行的递增 x 的任务。 在这种情况下,输出可能是从“Interrupted: x = 0”到“Interrupted: x = 9”的任何值,具体取决于线程何时被中断。

    请注意,在退出之前恢复线程的中断标志被认为是一种很好的做法,因为 否则run() 方法的调用者将看不到中断状态。

    现在,如果我们的任务完全执行至关重要,这样输出将始终为“x = 10”,这意味着该任务不支持取消,我们需要另一种方法:

      public void run() {
        int x = 0;
    
        boolean wasInterrupted = false; // <- This is the local variable to store the interruption status
    
        while (x < 10) {
    
          wasInterrupted = wasInterrupted || Thread.interrupted(); // not really needed in this case, but for the sake of completeness...
    
          try {
    
            Thread.sleep(1000); // <- Some interruptible operation
    
          } catch (InterruptedException e) {
            wasInterrupted = true;
          }
    
          x++;
        }
    
        System.out.println("x = " + x);
    
        if ( wasInterrupted ) {
          Thread.currentThread().interrupt();
        }
    
      }
    

    在这种情况下,即使在InterruptedException 之后,我们也会继续处理,直到任务完成。 为了保持良好状态,如果我们检测到中断,我们将该条件存储在 wasInterrupted 中,以便我们可以 在从方法返回之前正确设置中断标志。

    就是这个意思

    应该将中断状态保存在本地,并在返回前恢复。

    它说“应该”,因为我们没有严格要求以这种方式处理中断 - 我们也可以忽略任何 InterruptedException 完成我们的任务,然后返回。但是,这不是上面提到的良好做法,并且在某些情况下可能会导致问题。

    【讨论】:

      【解决方案3】:

      据我了解:长时间运行的服务本身不能或不应该被中断,它正在调用其他可以中断的方法。所以这个长时间运行的服务应该能够检测到这一点并通过方法或标志报告它。但它应该能够再次尝试操作,而不是仅仅抛出 InterruptedException。

      调用阻塞的方法意味着,当前执行被阻塞并等待直到阻塞方法返回一个值。这可以在一个循环中完成。然后你就知道方法调用是成功了还是被调用的方法被中断了。

      【讨论】:

        【解决方案4】:

        我没有这本书。但据我了解,如果活动中断(顺便说一下,睡眠不是中断信号。但是您可以通过中断信号将线程从睡眠中唤醒),活动需要按顺序保存其当前的动态数据(中断状态)恢复自身并从以前的状态恢复。例如;

        //Let's say you have integer data named "a"...
        a = 45646;
        
        //Normally after interruption, and terminating the activity data a is currently
        //referencing @memory will be released...
        
        //If you want to continue using data in a you have to save it somewhere
        // persistant(Let's say a file)
        void onInterruptionDetected()
        {
            saveToSomeFile(a, "temp.txt");
        }
        
        //After re-execution of activity(Assuming we need a's previous data in order to 
        // continue...), we can recover a's data(which is 45646) in previous interruption...
        void onResumeDetected()
        {
            a = loadFromFile("temp.txt")
        }
        

        希望这会有所帮助,我仍然很困,可能有错误:)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-12-25
          • 2022-10-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多