【问题标题】:How to determine where Java thread interrupts are coming from?如何确定 Java 线程中断来自何处?
【发布时间】:2015-02-08 03:44:51
【问题描述】:

我有一个 UI 自动化框架,它使用 TestNG 启动测试并使用 Selenium/WebDriver 运行页面。通常,我正在测试的页面会进行 AJAX 调用,这些调用会在返回时修改 DOM。在这些情况下,我使用 Selenium 显式等待来声明我希望在自动化继续之前满足的 DOM 条件(即:启用某些按钮)。

在内部 Selenium 的 FluentWait.until 方法通过每 500 毫秒轮询 DOM 以获取我的 ExpectedCondition 并在这些检查之间调用 Thread.sleep() 来处理此问题。

当我在 TestNG 套件中背靠背地运行两个测试时,这对于第一个测试非常有效,但是在每个后​​续测试的中途开始失败并出现 InterruptedException。这是一致的。异常如下所示:

Associated Throwable Type: class org.openqa.selenium.WebDriverException Associated Throwable Message: java.lang.InterruptedException: sleep interrupted

奇怪的是这里没有多线程。我已经禁用了 Selenium Grid、BrowserMob 代理和所有其他可能发生冲突的代码。我已经阅读了这两个问题:

https://stackoverflow.com/questions/24495176/why-is-thread-sleep-being-interrupted - 因未提供足够详细信息而关闭,但建议的答案之一指出应覆盖 Thread.interrupt 方法进行调试。

Who interrupts my thread? - 接受的答案还指出应该重写 Thread.interrupt 方法进行调试。

我对这个解决方案的问题是,在现有的 Thread.interrupt 方法中放置一个断点不会显示线程中断时的任何调用。这包括来自我所有第三方依赖项(IE:TestNG 和 Selenium)的调用。 调用这个线程中断的东西似乎在我的框架之外。

我还尝试在 FluentWait.until 调用之前的每一点调用 Thread.currentThread.isInterrupted() 并且它始终返回 false。我什至使用 IntelliJ 的评估函数来检查 Selenium 代码本身内部的 isInterrupted。 只有在 FluentWait.until 中发生 Thread.sleep 调用时才会中断该线程。

我已经在多个 Windows 构建服务器以及我的 Macbook 上看到了这种情况,因此这似乎不是特定于机器的。

我想了一会儿,这可能是由 TestNG 超时引起的,但是在我的套件中减少 TestNG 超时会产生与这些中断不同的行为。

目前我正在使用以下代码解决此问题,该代码吞下异常并恢复显式等待:

public static boolean waitForElementStatus(Stuff)
{

  /* snip - setup for ExpectedCondition (change) */

  long startSeconds = new Date().getTime() / 1000;
  long currentSeconds = startSeconds;
  long remainingSeconds = maxElementStatusChangeSeconds;
  WebDriverWait waitForElement = new WebDriverWait(driver, maxElementStatusChangeSeconds);
  boolean changed = false;
  boolean firstWait = true; // If specified time is 0 we still want to check once.
  out:while(firstWait || remainingSeconds > 0)
  {
    firstWait = false;
    Boolean exceptionThrown = false;
    try
    {
      waitForElement.until(change);
    }
    catch(Throwable t)
    {
      exceptionThrown = true;
      if(t.getCause()) != null
      {
        t = t.getCause(); // InterruptedException is wrapped inside a WebDriverException
      }
      if(t.getClass().equals(InterruptedException.class))
      {
        Thread.interrupted(); // clear interrupt status for this thread
        currentSeconds = new Date().getTime() / 1000;
        remainingSeconds = startSeconds + maxElementStatusChangeSeconds - currentSeconds;
        if(remainingSeconds > 0)
        {
          String warning = String.format("Caught unidentified interrupt inside Selenium " +
          "FluentWait.until call.  Swallowing interrupt and repeating call with [%s] seconds " + 
          "remaining.", remainingSeconds);
          CombinedLogger.warn(warning);
          waitForElement = new WebDriverWait(driver, remainingSeconds);
        }
        else
        {
          // If a timeout exception would have been thrown instead of the interruption then
          // we'll allow the WebDriverWait to execute one last time so it can throw the
          // timeout instead.
          waitForElement = new WebDriverWait(driver, 0);
        }
      }
      else if(haltOnFailure) // for any other exception type such as TimeoutException
      {
        CombinedLogger.error(stuff + "...FAILURE(HALTING)", t);
        break out;
      }
      else // for any other exception type such as TimeoutException
      {
        CombinedLogger.info(stuff + "...failure(non-halting)");
        break out;
      }
    }
    if(!exceptionThrown)
    {
      changed = true;
      CombinedLogger.info(stuff + "...success ");
      break out;
    }
  }
  return changed;
}

这种解决方法确实起作用,幸运的是,这些神秘的中断只是在之后偶尔发生(它们不会重复发生),因此测试能够继续进行。但是,我知道吞下 InterruptedException 是不好的形式。如果可能的话,我想确定这些中断发生的位置和原因,以便我可以结束它们而不是使用这个 hack。

简单地传播异常不是一种选择,因为这些测试需要继续运行而不是乖乖地崩溃。

是否有任何我可以使用的已知实用程序、JVM 参数或库来帮助我追踪由我无法控制的代码引起的 Java 线程中断?

2014 年 12 月 10 日更新:我捕获了两个线程转储。一个来自中断之前,一个来自中断之后。两者的唯一区别是被中断线程的行号(被中断后从try块到catch块)。不知道这告诉我什么,但这是数据:

Full thread dump (immediately before interrupt)

"TestNG@1359" prio=5 tid=0xc nid=NA runnable
  java.lang.Thread.State: RUNNABLE
	  at org.openqa.selenium.support.ui.FluentWait.until(FluentWait.java:232)
	  /* snip - company stuff */
	  at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-1)
	  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	  at java.lang.reflect.Method.invoke(Method.java:606)
	  at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:84)
	  at org.testng.internal.InvokeMethodRunnable.runOne(InvokeMethodRunnable.java:46)
	  at org.testng.internal.InvokeMethodRunnable.run(InvokeMethodRunnable.java:37)
	  at org.testng.internal.MethodInvocationHelper.invokeWithTimeoutWithNoExecutor(MethodInvocationHelper.java:240)
	  at org.testng.internal.MethodInvocationHelper.invokeWithTimeout(MethodInvocationHelper.java:229)
	  at org.testng.internal.Invoker.invokeMethod(Invoker.java:724)
	  at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:901)
	  at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1231)
	  at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:127)
	  at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:111)
	  at org.testng.TestRunner.privateRun(TestRunner.java:767)
	  at org.testng.TestRunner.run(TestRunner.java:617)
	  at org.testng.SuiteRunner.runTest(SuiteRunner.java:348)
	  at org.testng.SuiteRunner.access$000(SuiteRunner.java:38)
	  at org.testng.SuiteRunner$SuiteWorker.run(SuiteRunner.java:382)
	  at org.testng.internal.thread.ThreadUtil$2.call(ThreadUtil.java:64)
	  at java.util.concurrent.FutureTask.run(FutureTask.java:262)
	  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	  at java.lang.Thread.run(Thread.java:745)

"main@1" prio=5 tid=0x1 nid=NA waiting
  java.lang.Thread.State: WAITING
	  at sun.misc.Unsafe.park(Unsafe.java:-1)
	  at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:226)
	  at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:422)
	  at java.util.concurrent.FutureTask.get(FutureTask.java:199)
	  at java.util.concurrent.AbstractExecutorService.invokeAll(AbstractExecutorService.java:289)
	  at org.testng.internal.thread.ThreadUtil.execute(ThreadUtil.java:72)
	  at org.testng.SuiteRunner.runInParallelTestMode(SuiteRunner.java:367)
	  at org.testng.SuiteRunner.privateRun(SuiteRunner.java:308)
	  at org.testng.SuiteRunner.run(SuiteRunner.java:254)
	  at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
	  at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
	  at org.testng.TestNG.runSuitesSequentially(TestNG.java:1224)
	  at org.testng.TestNG.runSuitesLocally(TestNG.java:1149)
	  at org.testng.TestNG.run(TestNG.java:1057)
	  at org.testng.remote.RemoteTestNG.run(RemoteTestNG.java:111)
	  at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.java:204)
	  at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:175)
	  at org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:125)

"Thread-8@2432" daemon prio=5 tid=0x15 nid=NA runnable
  java.lang.Thread.State: RUNNABLE
	  at java.io.FileInputStream.readBytes(FileInputStream.java:-1)
	  at java.io.FileInputStream.read(FileInputStream.java:272)
	  at java.io.BufferedInputStream.fill(BufferedInputStream.java:235)
	  at java.io.BufferedInputStream.read1(BufferedInputStream.java:275)
	  at java.io.BufferedInputStream.read(BufferedInputStream.java:334)
	  - locked <0xe08> (a java.lang.UNIXProcess$ProcessPipeInputStream)
	  at java.io.FilterInputStream.read(FilterInputStream.java:107)
	  at org.apache.commons.exec.StreamPumper.run(StreamPumper.java:105)
	  at java.lang.Thread.run(Thread.java:745)

"Thread-7@2431" daemon prio=5 tid=0x14 nid=NA runnable
  java.lang.Thread.State: RUNNABLE
	  at java.io.FileInputStream.readBytes(FileInputStream.java:-1)
	  at java.io.FileInputStream.read(FileInputStream.java:272)
	  at java.io.BufferedInputStream.fill(BufferedInputStream.java:235)
	  at java.io.BufferedInputStream.read1(BufferedInputStream.java:275)
	  at java.io.BufferedInputStream.read(BufferedInputStream.java:334)
	  - locked <0xe09> (a java.lang.UNIXProcess$ProcessPipeInputStream)
	  at java.io.FilterInputStream.read(FilterInputStream.java:107)
	  at org.apache.commons.exec.StreamPumper.run(StreamPumper.java:105)
	  at java.lang.Thread.run(Thread.java:745)

"Thread-6@2424" prio=5 tid=0x13 nid=NA waiting
  java.lang.Thread.State: WAITING
	  at java.lang.Object.wait(Object.java:-1)
	  at java.lang.Object.wait(Object.java:503)
	  at java.lang.UNIXProcess.waitFor(UNIXProcess.java:261)
	  at org.apache.commons.exec.DefaultExecutor.executeInternal(DefaultExecutor.java:347)
	  at org.apache.commons.exec.DefaultExecutor.access$200(DefaultExecutor.java:46)
	  at org.apache.commons.exec.DefaultExecutor$1.run(DefaultExecutor.java:188)

"process reaper@2008" daemon prio=10 tid=0x10 nid=NA runnable
  java.lang.Thread.State: RUNNABLE
	  at java.lang.UNIXProcess.waitForProcessExit(UNIXProcess.java:-1)
	  at java.lang.UNIXProcess.access$500(UNIXProcess.java:54)
	  at java.lang.UNIXProcess$4.run(UNIXProcess.java:225)
	  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	  at java.lang.Thread.run(Thread.java:745)

"ReaderThread@645" prio=5 tid=0xb nid=NA runnable
  java.lang.Thread.State: RUNNABLE
	  at java.net.SocketInputStream.socketRead0(SocketInputStream.java:-1)
	  at java.net.SocketInputStream.read(SocketInputStream.java:152)
	  at java.net.SocketInputStream.read(SocketInputStream.java:122)
	  at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283)
	  at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325)
	  at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)
	  - locked <0xe0b> (a java.io.InputStreamReader)
	  at java.io.InputStreamReader.read(InputStreamReader.java:184)
	  at java.io.BufferedReader.fill(BufferedReader.java:154)
	  at java.io.BufferedReader.readLine(BufferedReader.java:317)
	  at java.io.BufferedReader.readLine(BufferedReader.java:382)
	  at org.testng.remote.strprotocol.BaseMessageSender$ReaderThread.run(BaseMessageSender.java:245)

"Finalizer@2957" daemon prio=8 tid=0x3 nid=NA waiting
  java.lang.Thread.State: WAITING
	  at java.lang.Object.wait(Object.java:-1)
	  at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
	  at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
	  at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:189)

"Reference Handler@2958" daemon prio=10 tid=0x2 nid=NA waiting
  java.lang.Thread.State: WAITING
	  at java.lang.Object.wait(Object.java:-1)
	  at java.lang.Object.wait(Object.java:503)
	  at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)

"Signal Dispatcher@2956" daemon prio=9 tid=0x4 nid=NA runnable
  java.lang.Thread.State: RUNNABLE

【问题讨论】:

  • "奇怪的是这里没有进行多线程。"很明显那是不是真的,因为你的睡眠被打断了。您的第一步应该是尝试隔离您的代码并用尽可能少的移动部件复制故障。首先,如果您编写一个简单的main() 方法直接调用您的@Test 方法(绕过TestNG),您还会被中断吗?
  • 这是一个公平的观点。总是有某种程度的多线程在进行。隔离这个失败可能需要我几天的时间。我会看看我能不能很快做到这一点。

标签: java selenium-webdriver testng


【解决方案1】:

从线程转储中无法推断出导致它的原因。 但实际上你不能过度依赖 Thread.sleep() ,它可能会因为已知/未知的原因而中断。OS 可能是后一种情况的原因。

Thread.sleep() 是少数认真对待中断的方法之一。由于线程在休眠时无法处理InterruptedException,因此需要处理它。

您现在正在做的可能不是一种解决方法,而是在这种情况下的一种方法,在这种情况下,我们不能没有 Thread.sleep()。

【讨论】:

  • 我的选择是:#1-这种解决方法(已经到位)。 #2-放弃我分配的任务来追踪神秘的中断,没有适当的工具或日志来确定它们发生的原因......毫不奇怪我选择了#1。
【解决方案2】:

有点过时,但我有类似的问题,在您之前发布的链接 (https://stackoverflow.com/a/2476246) 的帮助下,我在 Thread.interrupt() 方法中设置了一个断点。

显示中断是由 StoryManager.waitUntilAllDoneOrFailed() 方法造成的,该方法在整个故事设置超时后触发 future.cancel() 方法。

我的整个设置是:

page.getPageObject().withTimeoutOf(convertDuration(duration)).waitFor(by);

持续时间约为 60 秒。 (这一分钟是由于一些异步的东西)

configuredEmbedder().embedderControls().useStoryTimeouts("30");

stackTrace 是:

at java.util.concurrent.FutureTask.cancel(FutureTask.java:174)
      at org.jbehave.core.embedder.StoryManager.waitUntilAllDoneOrFailed(StoryManager.java:184)
      at org.jbehave.core.embedder.StoryManager.performStories(StoryManager.java:121)
      at org.jbehave.core.embedder.StoryManager.runStories(StoryManager.java:107)

这会中断 ThucydidesFluentWait.doWait() 中稍后的 Thread.sleep() 方法(基本上在下面的 Sleeper 实例方法 sleep() 中)

增加故事超时或正确设置 waitFor(...) 超时与故事超时可以解决我这边的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多