12

当我使用 Java 开发一个(学术)软件时,我被迫使用一个实现得很糟糕的 API。这意味着针对特定输入数据集对该 API 的调用有时永远不会返回。这一定是软件中的一个错误,因为它提供的算法是确定性的,有时它会在一组数据上终止,有时它会在同一组数据上陷入无限循环......

但是,修复 API 或重新实现它完全超出了范围。我什至有源代码,但 API 严重依赖于其他未记录且没有源代码的 API,并且到那时已经从网络上消失了(或者从未存在过?)。另一方面,这个“糟糕”的 API 是唯一一个解决了我遇到的特定问题的 API,所以我真的必须坚持下去。

问题是:处理表现得那么糟糕的 API 的最干净的方法是什么?当我遇到这个问题时,我决定将对 API 的调用放到一个单独的线程中。然后另一个线程偶尔会检查该线程是否已终止。如果过了一定时间,我会杀死正在使用的处理线程Thread#stop()并重新开始处理,希望它下次会返回。现在,我知道(并且当时知道)这种方法已被弃用,不能使用。但在这个学术背景下,让软件可能运行到未定义状态而不是让它崩溃是可以接受的。

忽略已经进入无限循环的处理线程也是不可接受的,因为它执行了一些相当 CPU 密集型的操作,这会显着降低用户机器的速度。

我没有尝试的另一种方法是在单独的进程而不是线程中开始处理,因为可以干净地杀死子进程而不会使软件处于不一致状态。或者新SwingWorker课程(尚不可用)可以完成这项工作吗?它有一个cancel()方法,但文档说它“尝试取消执行此任务”,所以它看起来也不是一种可靠的方法。

4

4 回答 4

11

我建议使用单独的过程。在 Java 中,一个线程基本上没有杀死第二个线程的安全方法,除非第二个线程定期检查它是否已被中断。

理想的解决方案是使用隔离物。隔离本质上是 Java 应用程序可以创建、管理和通信的私有虚拟机。特别是,父应用程序可以安全地杀死一个隔离及其所有线程。

参考:JSR-000121 应用程序隔离 API 规范 - 最终版本

问题是找到一个支持 Isolates 的 JVM。

于 2009-08-02T12:28:40.607 回答
5

我非常喜欢这种事情的单独流程。

产生一个子进程并等待结果。

如果 API 是非确定性的,则将计时器线程放入一个包装器中,将错误的 API 放入主程序中。

这样,子进程总是在给定时间内结束。它要么产生有用的结果,要么产生指示失败的系统退出代码。

于 2009-08-02T12:31:14.180 回答
2

@S.Lott 和 @Stephen C 的回答都关于如何处理这种情况,但我想补充一点,在非学术环境中,您还应该尽快更换 API一样实用。在我们被锁定在一个糟糕的 API 中的情况下,通常是由于其他原因而选择了一个出售的解决方案,随着时间的推移,我一直在努力用我自己的功能替换该功能。您的客户不会像您的教授那样宽容,因为他们实际上必须使用您的软件(或不使用!),而不仅仅是对其评分。

当然存在使用导管胶带是解决问题的足够选择。但是,当它导致您描述的如此糟糕的行为时,最好不要依赖它太久并开始进行真正的修复。

于 2009-08-02T12:43:32.653 回答
1

最好的办法是重新实现有问题的 API。但是,正如您所说,这是一个非常繁重的解决方案,可能超出范围。

如果可能的话,下一个最好的办法是包装 API。基本上,如果您可以提前确定导致失败的数据集是什么,您可以拒绝调用以保证确定性。听起来这也不适合您,因为您建议使用相同的数据集重复调用有时会在它在先前的调用中无限循环时终止。

鉴于上述选项不可用:
我认为您当前的线程解决方案最糟糕的选择。从性能的角度来看,为方法调用启动进程似乎太重了,以至于无法接受,即使它比使用线程更安全。 Thread.stop()是非常危险的,但如果你认真地防止任何锁定,你就可以逃脱它。

于 2009-08-02T12:35:05.570 回答