【问题标题】:Kill Java Thread for testing purposes杀死 Java 线程以进行测试
【发布时间】:2016-08-22 15:26:53
【问题描述】:

我目前正在测试一个使用 Java 实现的 ACID 语义的事务系统。系统具有回调处理程序,允许在事务期间执行任意调试操作,例如抛出一个模拟错误的异常。

但是,实际上可能发生的情况是线程死亡(例如,OutOfMemoryError)或整个 JVM 死亡(sigsev 或 OS 进程终止)。我可以看到在 JUnit 测试中模拟 JVM 进程的意外关闭是不可能的(因为 JUnit 在同一个 JVM 中运行)。但是杀死一个线程呢?我知道Thread#stop(),但它已被弃用,而且在线程中抛出了ThreadDeathError。我想模拟的是一个线程的瞬时“死亡”,它甚至阻止了catch(Throwable t) 子句的触发。这可以在 Java 中完成,而不会杀死 JUnit 测试运行程序吗?

同样,这不用于生产用途,仅用于严格测试。

【问题讨论】:

  • 在线程中显式抛出OutOfMemoryError 怎么样?
  • 好主意,但我不确定“手动”抛出的OutOfMemoryError 是否真的对 JVM 产生与“真正的”OoME 相同的效果。有人可能会争辩说,在到达 OoME 之前分配内存是一种想法,但这样一来 JUnit 测试环境也可能会被破坏......
  • 或者只是尝试加载非常大的文件。 OOM 很容易复现。
  • @Nambari 不,它实际上只杀死了一个线程。我不得不与发生这种情况的系统作斗争,它似乎继续工作,但一些重要的线程刚刚消失。
  • @Nambari - Sun JRE 1.4(我没有说它是最近的......)。当线程 B 尝试分配不可用的内存时,可以很容易地设置一个测试来查看线程 A 是否保持活动状态。

标签: java multithreading unit-testing jvm kill


【解决方案1】:

线程的瞬时“死亡”甚至阻止了 catch(Throwable t) 子句的触发。这可以用 Java 完成吗?

没有。这是不可能的。

JVM 使用操作系统线程,因此依赖于底层操作系统调度程序。所以在JVM级别,JVM不能抢占Java线程。 java 线程必须自愿挂起。 (这是常见的情况,但仍然是特定于平台和 JVM 的)

public static void main(String[] args) throws InterruptedException {
  Thread t1 = new Thread(new Runnable() {
    @Override
    public void run() {
      while (true) {
        int i = 0;
        i++;
      }
    }
  });
  t1.start();
  Thread.sleep(1000);
  t1.stop();
  Thread.sleep(1000);
  System.out.println(t1.isAlive());
}

在上面的代码中,stop() 起作用了。但是你可能想知道为什么没有wait()/sleep() 的无限循环看起来像“不自愿被暂停”?

stop() 在t1 中提出asynchronous exception。 t1线程可以通过轮询来检测异步异常。

来自Java Virtual Machine Specification:

stop 方法可以由一个线程调用,以影响另一个线程或指定线程组中的所有线程。它们是异步的,因为它们可能发生在其他线程或线程执行的任何时候。内部错误被认为是异步的

一个简单的实现可能会在每个控制传输指令的点轮询异步异常。

这意味着,在编译后的代码中,在jump 之前的while 循环结束时,有一条轮询异常的指令。如果异常存在,t1线程跳转到异常处理程序并自行停止。

因此,如果我让线程忽略所有异常,并且如果线程不自行停止,则不可能杀死 java 线程。

这是一个 stop() 不起作用的示例:

public static void main(String [] args) throws InterruptedException {
  Thread t1 = new Thread(new Runnable() {
    @Override
    public void run() {
      while (true) {
        try {
          while (true) {
            int i = 0;
            i++;
          }
        } catch (Throwable e) {
          e.printStackTrace();
        }
      }
    }
  });
  t1.start();
  Thread.sleep(1000);
  t1.stop();
  Thread.sleep(1000);
  System.out.println(t1.isAlive());
}

【讨论】:

  • 感谢您的解释。一开始我担心答案会是“不,这是不可能的”,但我学到了一些关于 JVM 的新东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-25
  • 2015-02-24
  • 2011-09-15
相关资源
最近更新 更多