java如何使外部方法可中断?
问题
我正在通过ExecutorService运行一些外部方法的多次调用。我希望能够中断这些方法,但不幸的是,它们自己并没有检查中断标志。有没有办法强制从这些方法引发异常
我知道,从任意位置抛出异常是potentially dangerous,在我的具体情况下,我愿意抓住这个机会,并准备处理后果
细节
我所说的“外部方法”是指一些来自外部库的方法,我不能修改它的代码(我可以,但只要发布新版本,它就会成为维护的噩梦)
外部方法的计算成本很高,不受IO限制,因此它们不会对常规中断做出响应,我也无法强制关闭通道、socket或其他东西。正如我前面提到的,它们也不检查中断标志
该代码在概念上类似于:
// my code
public void myMethod() {
Object o = externalMethod(x);
}
// External code
public class ExternalLibrary {
public Object externalMethod(Object) {
innerMethod1();
innerMethod1();
innerMethod1();
}
private void innerMethod1() {
innerMethod2();
// computationally intensive operations
}
private void innerMethod2() {
// computationally intensive operations
}
}
我试过的
^{
我尝试过的另一个选择是用一个特殊的“可中断”注释标记myMethod()
和类似的方法,然后使用AspectJ(我承认是新手)捕获那里的所有方法调用,比如:
@Before("call(* *.*(..)) && withincode(@Interruptable * *.*(..))")
public void checkInterrupt(JoinPoint thisJoinPoint) {
if (Thread.interrupted()) throw new ForcefulInterruption();
}
但是withincode
对匹配方法调用的方法不是递归的,因此我必须将此注释编辑到外部代码中
最后,这与a previous question of mine类似——尽管一个显著的区别是,现在我处理的是一个外部库
# 1 楼答案
我想到了以下奇怪的想法:
# 2 楼答案
你写道:
AspectJ的想法很好,但您需要
cflow()
或cflowbelow()
递归地匹配某个控制流(例如@Before("cflow(execution(@Interruptable * *(..)))")
)李>如果您的外部库有一个包名,您甚至可能不需要标记注释,您可以用
within()
来确定它。AspectJ非常强大,解决问题的方法通常不止一种。我建议使用它,因为它是为你这样的努力而制作的# 3 楼答案
如果内部方法具有类似的名称,则可以使用xml(spring/AspectJ)中的切入点定义而不是注释,因此不需要修改外部库的代码
# 4 楼答案
我想出了一个解决我问题的丑陋的办法。这并不漂亮,但它在我的情况下起作用,所以我把它贴在这里,以防它会帮助其他人
我所做的是分析我的应用程序的库部分,希望我能分离出一小组被重复调用的方法,例如一些
get
方法或equals()
或类似的方法;然后我可以在那里插入以下代码段:可以通过编辑库的代码手动插入,也可以通过编写适当的方面自动插入。请注意,如果库试图捕获并吞下
RuntimeException
,抛出的异常可能会被库不尝试捕获的其他异常所替换幸运的是,使用VisualVM,我能够找到一个单个方法,在我对库的特定使用过程中调用了很多次。添加上述代码段后,它现在可以正确响应中断
这当然是不可维护的,再加上没有什么能真正保证库在其他场景中会重复调用此方法;但它对我起作用,而且因为比较容易地对其他应用程序进行配置并在其中插入检查,所以我认为这是一个通用的,如果是丑陋的解决方案。p>
# 5 楼答案
这个解决方案也不容易,但它可以工作:使用Javassist或CGLIB,您可以在每个内部方法(可能是由main run()方法调用的方法)的开头插入代码,以检查线程是否处于活动状态,或者其他标志(如果是其他标志,您还必须添加它,以及设置它的方法)
我建议使用Javassist/CGLIB,而不是通过代码扩展类,因为您提到它是外部的,您不想更改源代码,而且将来可能会更改。因此,在运行时添加中断检查将适用于当前版本,也适用于将来的版本,即使内部方法名称发生更改(或其参数、返回值等)。您只需获取该类并在每个非run()方法的方法的开头添加中断检查
# 6 楼答案
一种选择是:
id
字段,则可以识别执行该字段的线程。)李>虽然我不认为停止的线程会严重干扰执行器(毕竟它们应该是故障安全的),但有一种替代解决方案不涉及停止线程
如果您的任务不修改系统其他部分中的任何内容(这是一个合理的假设,否则您不会试图将其击落),那么您可以使用JDI弹出不需要的堆栈帧并正常退出任务
这是包装所有其他runnable的runnable。它存储对正在执行的线程的引用(稍后将很重要)和一个id,以便我们可以通过JDI调用找到它
作为参考,我测试了“库”调用:
您可以通过使用命令行选项
-agentlib:jdwp=transport=dt_socket,server=y,address=localhost:8000,timeout=5000,suspend=n
启动Main
类来尝试。它有两个警告。首先,如果正在执行本机代码(thisObject
帧的值为空),则必须等待它完成。其次,不会调用finally
块,因此各种资源可能会泄漏