如何确定Java线程中断的来源?

时间:2014-12-10 03:06:02

标签: java selenium-webdriver testng

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

内部Selenium的FluentWait.until方法通过每隔500ms轮询一次我的ExpectedCondition并在这些检查之间调用Thread.sleep()来处理这个问题。

当我在TestNG套件中背靠背运行两个测试时,这对第一个测试完全正常,但是在每个后​​续测试的中途开始失败并出现InterruptedException。这是一致的。例外情况如下:

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

奇怪的是,这里没有多线程。我已禁用Selenium Grid,BrowserMob Proxy以及可能存在冲突的所有其他代码。我已经阅读了这两个问题:

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的evaluate函数来检查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是不好的形式。如果可能的话,我想确定这些中断发生的地点和原因,以便我可以结束它们而不是使用这个黑客。

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

我可以使用哪些已知的实用程序,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

2 个答案:

答案 0 :(得分:0)

从线程转储中可以推断出没有太多可以推断它的原因。 但实际上你不能过多依赖Thread.sleep(),它可能因已知/未知原因而中断.OS可能是后一种情况的原因。

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

你现在所做的可能不是一种解决方法,而是在这种情况下的一种方式,我们离不开Thread.sleep()。

答案 1 :(得分:0)

有点过时,但我有类似的问题,在你之前发布的链接(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实例方法下面睡眠())

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