0

我们的生产箱上发生了一件奇怪的事情。代码功能:UI servlet 对用户正在操作的文档对象进行监视器锁定并对其执行一些计算。获取监控锁是为了防止同一个文档对象同时被多个用户同时修改。

Prod 中观察到的问题:很少有用户操作超时。

日志分析:超时用户操作对应的线程在获取文档对象上的监控锁之前正在打印所有日志。然后有一个超过 1 小时的间隙,线程没有在日志中浮出水面,然后它突然变得活跃并进行计算并尝试发回一个响应,该响应显然是错误的,因为 HTTP 请求已经超时。我们检查了日志和代码,可以确认没有其他线程获得了对该特定文档对象的监控锁。所以锁在有问题的地方是没有争议的。

可能的问题是什么?是否只是线程在遇到同步块时进入可运行状态,并且在接下来的 60-80 分钟内,CPU 从来没有机会运行这个特定的可运行线程?

4

1 回答 1

0

确保应用程序代码不会通过Thread.setPriority()方法或类似方法弄乱线程优先级。如果您使用 IntelliJ 之类的 IDE 并且 Java 源可用,并且假设您可以在开发机器中本地运行应用程序和相关流程,您可以设置断点Thread.setPriority()以查看是否在任何地方调用它。这是 Java Concurrency in Practice, Goetz 2006 的摘录,关于如果您尝试手动设置线程优先级可能会有怎样的不可预知的行为:

10.3.1. 当一个线程永远被拒绝访问它需要的资源以取得进展时,就会发生饥饿。最常见的资源匮乏是 CPU 周期。Java 应用程序中的饥饿可能是由于线程优先级使用不当造成的。它也可能是由于在持有锁的情况下执行非终止构造(无限循环或不终止的资源等待)引起的,因为需要该锁的其他线程将永远无法获得它。Thread API 中定义的线程优先级仅仅是调度提示。Thread API 定义了十个优先级,JVM 可以根据需要将其映射到操作系统调度优先级。此映射是特定于平台的,因此两个 Java 优先级可以映射到一个系统上的相同操作系统优先级和另一个系统上的不同操作系统优先级。一些操作系统的优先级少于十个,在这种情况下,多个 Java 优先级映射到同一个操作系统优先级。操作系统调度程序竭尽全力提供超出 Java 语言规范要求的调度公平性和活跃性。在大多数 Java 应用程序中,所有应用程序线程都具有相同的优先级,即 Thread。NORM_PRIORITY。线程优先级机制是一种钝器,改变优先级会产生什么效果并不总是很明显;提高线程的优先级可能什么都不做,或者可能总是导致一个线程优先于另一个线程被调度,从而导致饥饿。抵制调整线程优先级的诱惑通常是明智的。一旦你开始修改优先级,您的应用程序的行为变得特定于平台,并且您引入了饥饿的风险。您经常可以通过在奇怪的地方出现 Thread.sleep 或 Thread.yield 调用来发现试图从优先级调整或其他响应问题中恢复的程序,以尝试为较低优先级的线程提供更多时间。 [5]

于 2019-03-10T21:39:55.853 回答