如何使外部方法可以中断?

Oak*_*Oak 30 java aop multithreading aspectj executorservice

问题

我正在通过ExecutorService运行一些外部方法的多次调用.我希望能够中断这些方法,但遗憾的是它们不会自行检查中断标志.有什么办法可以强制从这些方法中引发异常吗?

我知道从任意位置抛出异常是有潜在危险的,在我的具体情况下,我愿意抓住这个机会并准备应对后果.

细节

"外部方法"我的意思是来自外部库的一些方法,我不能修改它的代码(我可以,但是每当新版本发布时,这将使它成为维护的噩梦).

外部方法计算成本高,不受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
    }
}
Run Code Online (Sandbox Code Playgroud)

我试过的

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

我尝试过的另一个选择是myMethod()使用特殊的"Interruptable"注释标记和类似的方法,然后使用AspectJ(我无疑是新手)来捕获所有方法调用 - 类似于:

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

但是withincode对于匹配方法调用的方法不是递归的,所以我必须将这个注释编辑到外部代码中.

最后,这类似于我之前的一个问题 - 虽然一个显着的区别是现在我正在处理一个外部库.

mha*_*ler 5

我想到了以下奇怪的想法:

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


Oak*_*Oak 1

我为我的问题找到了一个丑陋的解决方案。它并不漂亮,但它适用于我的情况,所以我将其发布在这里,以防对其他人有所帮助。

我所做的是分析我的应用程序的库部分,希望我可以隔离一小组重复调用的方法 - 例如一些get方法或equals()类似的东西;然后我可以在那里插入以下代码段:

if (Thread.interrupted()) {
    // Not really necessary, but could help if the library does check it itself in some other place:
    Thread.currentThread().interrupt();
    // Wrapping the checked InterruptedException because the signature doesn't declare it:
    throw new RuntimeException(new InterruptedException());
}
Run Code Online (Sandbox Code Playgroud)

通过编辑库的代码手动插入它,或者通过编写适当的方面自动插入。请注意,如果库尝试捕获并吞下 a RuntimeException,则抛出的异常可能会被库不尝试捕获的其他内容替换。

对我来说幸运的是,使用VisualVM,我能够在我对库的具体使用过程中找到一个被调用次数非常多的方法添加上述代码段后,现在可以正确响应中断了。

这当然是不可维护的,而且没有什么能真正保证库会在其他场景中重复调用此方法;但它对我有用,并且由于分析其他应用程序并在那里插入检查相对容易,我认为这是一个通用的(尽管丑陋的)解决方案。