如何使外部方法可中断?

问题

我正在通过执行器服务运行对某些外部方法的多次调用。我希望能够中断这些方法,但不幸的是,它们不会自己检查中断标志。有没有办法强制从这些方法引发异常?

我知道从任意位置抛出异常是潜在的危险,在我的具体案例中,我愿意抓住这个机会并准备处理后果。

通过“外部方法”,我的意思是来自外部库的一些方法,我不能修改它的代码(好吧,我可以,但每当新版本发布时,这都会使它成为维护的噩梦)。

外部方法在计算上很昂贵,不是IO绑定的,所以它们不响应常规中断,我无法强制关闭通道或套接字或其他内容。正如我之前提到的,他们也不检查中断标志。

该代码在概念上类似于:

// 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
    }
}

我尝试过什么

从理论上讲,Thread.stop()将执行我想要的操作,但它不仅已弃用,而且也仅适用于实际线程,而我正在处理执行器任务(也可能与将来的任务共享线程,例如在线程池中工作时)。但是,如果没有找到更好的解决方案,我将转换我的代码以使用老式的线程,并使用此方法。

我尝试过的另一种选择是使用特殊的“可中断”注释标记和类似的方法,然后使用AspectJ(我承认我是新手)来捕获那里的所有方法调用 - 如下所示:myMethod()

@Before("call(* *.*(..)) && withincode(@Interruptable * *.*(..))")
public void checkInterrupt(JoinPoint thisJoinPoint) {
    if (Thread.interrupted()) throw new ForcefulInterruption();
}

但是不是由匹配方法调用的方法的递归,因此我必须将此注释编辑到外部代码中。withincode

最后,这与我之前的问题类似 - 尽管一个显着的区别是现在我正在处理外部库。


答案 1

我脑海中浮现出以下奇怪的想法:

  • 使用字节码修改库(如 Javassist)在字节码内的各个点引入中断检查。仅仅在方法的开头可能还不够,因为你提到这些外部方法不是递归的,所以你可能想在任何时候强制停止它们。在字节码级别执行此操作也会使其响应非常灵敏,例如,即使外部代码在循环或其他内容中运行,也可以引入中断检查。但是,这将增加一些开销,因此整体性能会降低。
  • 为外部代码启动单独的进程(例如,单独的 VM)。中止进程可能比其他解决方案更容易编码。缺点是您需要在外部代码和代码之间使用某种通信通道,例如IPC,套接字等。第二个缺点是,您需要更多资源(CPU、内存)来启动新 VM,并且它可能特定于环境。如果您使用外部代码启动几个任务,而不是数百个任务,这将起作用。此外,性能会受到影响,但计算本身将与原始计算一样快。可以使用java.lang.Process.destroy()强制停止进程
  • 使用自定义安全管理器,它对每个 checkXXX 方法执行中断检查。如果外部代码以某种方式调用特权方法,则在这些位置中止可能就足够了。一个例子是java.lang.SecurityManager.checkPropertyAccess(String),如果外部代码定期读取系统属性。

答案 2

这个解决方案也不容易,但它可以工作:使用Javassist或CGLIB,你可以在每个内部方法的开头插入代码(可能是由main run()方法调用的那些),以检查线程是否处于活动状态,或者其他一些标志(如果它是其他标志,你也必须添加它, 以及设置它的方法)。

我建议使用Javassist/CGLIB,而不是通过代码扩展类,因为你提到它是外部的,你不想改变源代码,而且将来可能会改变。因此,在运行时添加中断检查将适用于当前版本,也适用于将来的版本,即使内部方法名称(或其参数,返回值等)发生更改也是如此。你只需要接受类,并在不是run()方法的每个方法的开头添加中断检查。