Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/395.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何使外部方法可中断? 问题_Java_Multithreading_Aop_Aspectj_Executorservice - Fatal编程技术网

Java 如何使外部方法可中断? 问题

Java 如何使外部方法可中断? 问题,java,multithreading,aop,aspectj,executorservice,Java,Multithreading,Aop,Aspectj,Executorservice,我正在通过一个。我希望能够中断这些方法,但不幸的是,它们自己并没有检查中断标志。有没有办法强制从这些方法引发异常 我知道,在我的具体情况下,从任意位置抛出异常是我愿意抓住这个机会并准备处理后果的 细节 我所说的“外部方法”是指一些来自外部库的方法,我不能修改它的代码(我可以,但只要发布新版本,它就会成为维护的噩梦) 外部方法的计算成本很高,不受IO限制,因此它们不会对常规中断做出响应,我也无法强制关闭通道、套接字或其他东西。正如我前面提到的,它们也不检查中断标志 该代码在概念上类似于: // m

我正在通过一个。我希望能够中断这些方法,但不幸的是,它们自己并没有检查中断标志。有没有办法强制从这些方法引发异常

我知道,在我的具体情况下,从任意位置抛出异常是我愿意抓住这个机会并准备处理后果的

细节 我所说的“外部方法”是指一些来自外部库的方法,我不能修改它的代码(我可以,但只要发布新版本,它就会成为维护的噩梦)

外部方法的计算成本很高,不受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
    }
}
@Before("call(* *.*(..)) && withincode(@Interruptable * *.*(..))")
public void checkInterrupt(JoinPoint thisJoinPoint) {
    if (Thread.interrupted()) throw new ForcefulInterruption();
}
@Before("call(* *.*(..)) && withincode(@Interruptable * *.*(..))")
public void checkInterrupt(JoinPoint thisJoinPoint) {
    if (Thread.interrupted()) throw new ForcefulInterruption();
}
我试过的 从理论上讲,它可以做我想做的事情,但它不仅已被弃用,而且仅适用于实际的线程,而我正在处理执行器任务(也可能与将来的任务共享线程,例如在线程池中工作时)。然而,如果找不到更好的解决方案,我会将代码转换为使用老式线程,并使用此方法

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

// 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
    }
}
@Before("call(* *.*(..)) && withincode(@Interruptable * *.*(..))")
public void checkInterrupt(JoinPoint thisJoinPoint) {
    if (Thread.interrupted()) throw new ForcefulInterruption();
}
@Before("call(* *.*(..)) && withincode(@Interruptable * *.*(..))")
public void checkInterrupt(JoinPoint thisJoinPoint) {
    if (Thread.interrupted()) throw new ForcefulInterruption();
}
但是
withincode
对于匹配方法调用的方法不是递归的,因此我必须将此注释编辑到外部代码中


最后,这类似于-尽管一个显著的区别是我现在处理的是一个外部库。

如果内部方法具有类似的名称,那么您可以使用xml(spring/AspectJ)中的切入点定义而不是注释,因此不需要对外部库进行代码修改。

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


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

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

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

一个很好的解决方案是使用一个支持“轻量级线程”的任意暂停/停止的库,比如代替executor服务,尽管这可能有点过分

虽然我从未使用过Akka,也无法确认它是否能如您所期望的那样工作,但文档说明有一种方法可以停止参与者。

一个选项是:

  • 查找正在运行任务的线程。这不是小事,但由于您可以访问所有堆栈帧,因此它当然是可行的。(如果在任务对象中放置唯一的
    id
    字段,您将能够识别执行它的线程。)
  • 虽然我不认为停止的线程会严重干扰执行器(毕竟它们应该是故障安全的),但有一种替代解决方案不涉及停止线程

    如果您的任务不修改系统其他部分中的任何内容(这是一个合理的假设,否则您不会试图将其击落),那么您可以使用JDI从系统中弹出不需要的堆栈帧
    @Before("call(* *.*(..)) && withincode(@Interruptable * *.*(..))")
    public void checkInterrupt(JoinPoint thisJoinPoint) {
        if (Thread.interrupted()) throw new ForcefulInterruption();
    }