线程中断状态被清除 - 可能的 Java 错误

Posted

技术标签:

【中文标题】线程中断状态被清除 - 可能的 Java 错误【英文标题】:Thread interrupt status getting cleared - possible Java bug 【发布时间】:2013-01-11 13:09:55 【问题描述】:

这是参考Path#register方法。如果一个线程正在运行一个包含该方法的块并且另一个线程预先中断它。然后发现该方法清除了中断状态。

文档中没有提到它清除线程的中断状态。

复制

import java.io.*;
import java.nio.file.*;

import static java.nio.file.LinkOption.*;
import static java.nio.file.StandardWatchEventKinds.*;

import java.nio.file.attribute.*;

public class WatchDir 

    private final WatchService watcher;
    
    private void register(Path dir) throws IOException 
        // interrupt itself
        Thread.currentThread().interrupt();
        boolean before = Thread.currentThread().isInterrupted();
        WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
        boolean after = Thread.currentThread().isInterrupted();

        if (before) 
            if (!after) 
                System.out.println("--------------------BUG-------------------");
                System.out.format("Interrupt Status: true before making call to Path#register for folder folder: %s\n", dir);
                System.out.format("Interrupt Status: false after making call to Path#register for folder folder: %s\n", dir);
                System.out.println("The interrupt status was cleared by `register` (Unexpected Behavior).");
                System.exit(0);
             else 
                System.out.println("Not a bug on your machine. The interrupt was not cleared after call to Path#register. Works as expected");
                System.out.println("Works well on your machine. Didn't work for me on Windows and Ubuntu with Java 7");
            
        
    

    /**
     * Creates a WatchService and registers the given directory
     */
    WatchDir(Path dir) throws IOException 
        this.watcher = FileSystems.getDefault().newWatchService();
        register(dir);
    
    
    public static void main(String[] args) throws Exception 
        // register directory and process its events
        Path dir = Paths.get(".");
        new WatchDir(dir);
    

以上可以观察到调用register函数后中断状态被清除。示例输出:

--------------------BUG-------------------
Interrupt Status: true before making call to Path#register for folder folder: .
Interrupt Status: false after making call to Path#register for folder folder: .
The interrupt status was cleared by `register` (Unexpected Behavior).

出现此问题是因为发现即使在请求关闭后服务仍处于活动状态。有什么想法吗?

edit:事实证明它只发生在 Windows 和 Linux 中。 Mac 的行为符合预期。我的操作系统:Win 7 64 位。 JDK 1.7.0_11。 还可以在以下位置找到:Ubuntu 14.04 Java 1.7.0_45-b18

【问题讨论】:

This could be the cuplrit - 它在某个阶段被register method specific to Windows Paths调用。 您应该将该评论作为@assylias 的答案。 @Gray 我没有看详细信息,所以不能 100% 确定这是它(但它会有意义)。 @assylias 或 Jatin,你要提交错误报告吗? @MichaelDeardeuff 我没有。 【参考方案1】:

问题可能在于catch 块:

 catch (InterruptedException ex) 
  Logger.getLogger(WatchDir.class.getName()).log(Level.SEVERE, null, ex);

尝试添加

// Restore the interrupted status
Thread.currentThread().interrupt();

catch 块中。

有关此主题的更多信息,请参阅帖子"Java theory and practice: Dealing with InterruptedException"

【讨论】:

包含该行的线程正在中断另一个线程,而不是自己被中断。因此从不调用 catch 块。基本上它是调用者线程。 那么我的建议是深入方法调用并检查InterruptedException 在没有正确设置中断标志的情况下处理的位置。 没错,这就是问题所在。我的代码没有任何 InterruptedException。任何使用方法的文档都没有描述它 @BorisPavlović 代码确实吞下了(忽略)一个 InterruptedException - 请参阅我在上面的 cmets 中发布的链接。 @assylias 我已将其注册为错误。但我不知道他们会认为这是一个微不足道的问题。【参考方案2】:

Threadinterrupted 标志不是一个单独依赖的好字段。您应该同时监视interrupted 标志并注意中断。

请参阅The Law of the Sabotaged Doorbell 进行精彩讨论。

一个特别中肯的引述来自here(我的重点):

interrupt ... 如果此线程在调用 Object 类或 join 的 wait()、wait(long) 或 wait(long, int) 方法时被阻塞(), join(long), join(long, int), sleep(long), or sleep(long, int),这个类的方法,则其中断状态将被清除收到一个 InterruptedException。

【讨论】:

谢谢:)。我用一个小技巧解决了这个问题。但严格来说,为了这个问题 - 它清除但不会抛出 InterruptedException。这就是全部问题 @Jatin:您应该发布您的解决方案作为答案。【参考方案3】:

这很简单......一旦你在同一个线程中引用了中断状态。它会被清除。

如果中断状态是从另一个线程引用的,它不会被清除

【讨论】:

docs.oracle.com/javase/7/docs/api/index.html?java/lang/… 调用isInterrupted 方法的文档说:线程的中断状态不受此方法的影响。所以没有 如果您检查此docs.oracle.com/javase/tutorial/essential/concurrency/… 在中断状态标志部分,他们提到中断机制是使用称为中断状态的内部标志实现的。调用 Thread.interrupt 设置此标志。 “当一个线程通过调用静态方法Thread.interrupted检查中断时,中断状态被清除。非静态isInterrupted方法,由一个线程用来查询另一个线程的中断状态,不会改变中断状态标志。” 看起来你误读了代码:)。我用过Thread.currentThread().isInterrupted() 而不是interrupted()

以上是关于线程中断状态被清除 - 可能的 Java 错误的主要内容,如果未能解决你的问题,请参考以下文章

Java中断机制

详解线程interrupt()方法

关于Java多线程-interrupt()interrupted()isInterrupted()解释

线程中断

线程中断:Thread类中interrupt()interrupted()和 isInterrupted()方法详解

Java多线程学习之线程的取消与中断机制