Java中的阻塞方法到底是什么?

Fat*_*lan 3 java concurrency

阻塞方法的定义非常明确。还是有件事让我困惑。在 Java 程序中,如果我创建一个线程并尝试take()从一个空的线程开始BlockingQueue,根据调试器,该线程将变为 WAITING 状态。这正如预期的那样。

另一方面,如果我创建一个线程并尝试调用类accept()的方法ServerSocket(根据 JavaDoc,这也是一个阻塞代码),我会看到该线程始终处于 RUNNING 状态。

我期望在 Java 中将阻塞方法与监视器一起停放。如果一个方法像 一样是阻塞的ServerSocket::accept,为什么这个方法没有进展接受行并且仍然具有 RUNNING 状态?

rzw*_*oot 5

有“阻塞调用”/“阻塞方法”的概念,人们可能会在对话中使用它,或者作为教程可能使用它,甚至作为 javadoc 可能使用它。

然后是具体且极其精确定义java.lang.Thread的状态BLOCKING

正如您已经通过测试发现的那样,这两个概念并不一致。BLOCKING 状态实际上意味着“通过可以阻塞的机制列表进行阻塞”(主要是等待获取监视器,即当您进入阻塞synchronized(x)或尝试再次从调用中获取时会发生什么x.wait():在这两种情况下,线程需要成为拥有所指向对象上的锁的“线程” x,如果由于另一个线程持有它而无法做到这一点,则该线程的状态将变为 BLOCKING。

javadoc中对此进行了详细说明。这是引用:

等待监视器锁而被阻塞的线程就处于这种状态。

synchronized(“监视器锁”是 JVM 术语,指与和一起使用的机制obj.wait/notify/notifyAll仅此而已)。

继续阅读该页面的完整 javadoc,因为这些状态的详细描述通常准确地说明了哪些方法可以导致这些状态。

这可以让您弄清楚如果您编写此代码:

synchronized (foo) {
   foo.wait();
}
Run Code Online (Sandbox Code Playgroud)

那么在最坏的情况下,该线程会经历这些状态)

  • RUNNING -> BLOCKED(另一个线程已经在synchronized(foo)块中)。
  • 阻塞 -> 运行(其他线程已完成)
  • RUNNING -> WAITING(调用 obj.wait(),现在等待通知)
  • WAITING -> BLOCKED(我们已收到通知,但线程仍然无法继续,直到再次拾取该监视器,这就是等待和通知的工作原理)。
  • BLOCKED -> RUNNING(锁定 foo)

那么为什么我的 I/O 状态是 RUNNING 呢?

不幸的是,与 I/O 相关的阻塞是高度未定义的行为

不过,我可以解释一个常见的场景(即大多数操作系统、硬件和 JVM 提供商的组合最终会做什么)。

未定义行为的原因与 RUNNING 状态的原因相同:Java 应该在许多硬件/操作系统组合上运行,而大多数 I/O 最终只是 java 'farms out' 的东西' 到操作系统,操作系统就会执行操作系统将要执行的任何操作。Java 本身并不管理这些线程的状态,它只是调用操作系统做某件事,然后操作系统最终会阻塞、等待等。Java 不需要管理它;并非所有操作系统都允许 java 尝试更新此状态,并且在任何情况下,对于 java 进程来说它都没有任何意义,它只会减慢速度,并添加到需要更新的代码堆中为每个运行 java 的操作系统编写的自定义文件”,而您希望这些文件保持在很小的范围内。唯一的好处是您可以编写可以以编程方式检查线程状态的代码……这更多的是代理的工作,而不是同一虚拟机内运行的 java 代码的工作。

但是,正如我所说,大多数情况下是未定义的。不要依赖x.get()套接字的 InputStream 将使线程保持在 RUNNING 状态这一事实。

当您尝试interrupt()当前正在 I/O 模式下等待的线程时,会发生类似的情况。这意味着当前正在等待数据的 I/O 调用可能会立即退出,并出现一些 IOException(不过,不是 InterruptedException,这是可以保证的;InterruptedException 已检查,InputStream.read() 未声明抛出它,因此,它不会't) - 或者,它可能什么也不做。取决于操作系统、java 版本、硬件、供应商等。