1

我已经被一个不会合作并干净退出的java进程所困扰(它会被埋在一些我无法控制的低级库中)。我现在正在测试一种看门狗,它在某个预先确定的时间对进程实施硬停止,ScheduledStop. 该看门狗是一个单独的类,它运行一个独立的线程,如果预定的停止时间到达,它将终止整个进程。通常,所有线程都应该在该硬停止时间之前很好地返回,并且程序会优雅地退出。但是,如果有必要,进程会自行终止,文件锁会被释放等。所有这些都在 Linux 上运行。

我似乎记得,即使System.exit(0)不是万无一失的(我认为如果某些关闭挂钩卡住,该过程可能会保持活动状态),所以我编造了一些类似的东西:

int pid = MyUtil.getPID();
Runtime.getRuntime().exec(new String[]{"kill", "-9", String.valueOf(pid)});

现在,我想用一些非常不合作的线程来测试它,可能还有一些关闭钩子,这些钩子是为了测试而设计的,效果不佳。

下面的最初的 NastyThread 并不是那么讨厌......它忽略了InterruptionException,但不阻止System.exit(0)。我怎样才能让我的虚拟机进入一个甚至exit()不会终止的状态?

另一个问题是,虽然看门狗线程在理论上是独立的,但其他线程完全抢占它,从而挫败预定停止的条件是什么?

如有必要,我可以启动一个单独的进程(例如一个简单的 perl 脚本),在某个指定时间杀死父进程(java 进程)。

/**
 * A Runnable that runs forever and ignores InterruptedException.
 */
private static class NastyThread implements Runnable {
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                System.out.println("Received and ignoring "+e);
                System.out.flush();
            }
            System.out.println(ScheduledStop.getInstance().toString());
        }
    }
}
4

2 回答 2

0

好的,我按照@Lawrey 的建议添加了一个 SecurityManager。不知何故,应用程序仍然可以愉快地System.exit(0)

但是后来,我添加了一个关闭钩子,它只会启动一个更讨厌的线程!这就是诀窍,现在我的ScheduledStop课程可以被测试(并且可以工作)。这是关闭挂钩:

Runtime.getRuntime().addShutdownHook(new Thread(
    new NastyThread("nasty-1 (shutdown-hook)")
));

这是测试的输出:

gp> ~$ java -cp "$CLASSPATH" com.somepackage.ScheduledStop
nasty-0: Stop scheduled at 20130916-111611-PDT (in PT4.488S)
nasty-0: Stop scheduled at 20130916-111611-PDT (in PT3.939S)
nasty-0: Stop scheduled at 20130916-111611-PDT (in PT3.437S)
main would like to exit(0).
nasty-0: Stop scheduled at 20130916-111611-PDT (in PT2.936S)
nasty-1 (shutdown-hook): Stop scheduled at 20130916-111611-PDT (in PT2.487S)
nasty-0: Stop scheduled at 20130916-111611-PDT (in PT2.434S)
nasty-1 (shutdown-hook): Stop scheduled at 20130916-111611-PDT (in PT1.985S)
nasty-0: Stop scheduled at 20130916-111611-PDT (in PT1.932S)
nasty-1 (shutdown-hook): Stop scheduled at 20130916-111611-PDT (in PT1.484S)
nasty-0: Stop scheduled at 20130916-111611-PDT (in PT1.431S)
nasty-1 (shutdown-hook): Stop scheduled at 20130916-111611-PDT (in PT0.981S)
nasty-0: Stop scheduled at 20130916-111611-PDT (in PT0.928S)
nasty-1 (shutdown-hook): Stop scheduled at 20130916-111611-PDT (in PT0.479S)
nasty-0: Stop scheduled at 20130916-111611-PDT (in PT0.426S)
Hard stop (kill -9 self=6967).
zsh: killed     java -cp "$CLASSPATH" com.somepackage.ScheduledStop

FWIW,这是整个“测试”代码(不是真正的测试,只是将 ScheduledStop 带到街区周围一小段车程):

/*----------------------------------
 * all the code below is for testing
 */
/**
 * A Runnable that runs forever and ignores InterruptedException.
 */
private static class NastyThread implements Runnable {
    private final String name;

public NastyThread(String name) {
    super();
    this.name = name;
}

@Override
public void run() {
    while (true) {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            System.out.println(name+": received and ignoring "+e);
            System.out.flush();
        }
        System.out.println(name+": "+ScheduledStop.getInstance().toString());
    }
}
}

@SuppressWarnings("serial")
private static class ExitTrappedException extends SecurityException { }

private static void forbidSystemExitCall() {
    final SecurityManager securityManager = new SecurityManager() {
        public void checkPermission(Permission permission ) {
            if ("exitVM".equals(permission.getName())) {
                throw new ExitTrappedException() ;
            }
        }
    };
    try {
        System.setSecurityManager(securityManager);
    } catch (Exception e) {
        System.err.println("got: "+e);
    }
}

@SuppressWarnings("unused")
private static void enableSystemExitCall() {
    System.setSecurityManager( null ) ;
}

/**
 * Spawn an un-cooperative thread, then kill itself after a few seconds.
 */
public static void main(String[] args) throws IOException {
    final File lockFile = new File("testStop.lock");
    final Period runFor = Period.seconds(5);
    try (HplFileLock lock = FileUtil.getFileLockOrExit(lockFile, 0)) {
        ScheduledStop.getInstance().runFor(runFor);
    } catch (Exception e) {
        System.err.println("Exception: " + e);
        System.err.flush();
        System.exit(-1);
    }

    // disallow System.exit()
    forbidSystemExitCall();

    // launch a pesky thread that ignores interruption
    Runnable r = new NastyThread("nasty-0");
    new Thread(r).start();

    // further, install a shutdown hook that just launches one more NastyThread!
    Runtime.getRuntime().addShutdownHook(new Thread(new NastyThread("nasty-1 (shutdown-hook)")));

    // now wait 2 seconds and try to exit
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("main would like to exit(0).");
    System.out.flush();
    System.exit(0);
}
于 2013-09-16T18:29:41.370 回答
0

您可以将 SecurityManager 设置为在调用 System.exit() 时忽略或抛出错误。

顺便提一句

while(true) Thread.yield();

或者

for(;;);

也会忽略中断。

于 2013-09-16T17:42:42.700 回答