How can I make external methods interruptable?

后端 未结 8 911
南旧
南旧 2021-02-05 02:32

The Problem

I\'m running multiple invocations of some external method via an ExecutorService. I would like to be able to interrupt these methods, but unfortunately the

相关标签:
8条回答
  • 2021-02-05 03:13

    I have hacked an ugly solution to my problem. It's not pretty, but it works in my case, so I'm posting it here in case it will help anyone else.

    What I did was profile the library parts of my application, hoping that I could isolate a small group of methods which are called repeatedly - for example some get methods or equals() or something along these lines; and then I could insert the following code segment there:

    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());
    }
    

    Either inserting it manually by editing the library's code, or automatically by writing an appropriate aspect. Notice that if the library attempts to catch and swallow a RuntimeException, the thrown exception could be replaced with something else the library doesn't try to catch.

    Luckily for me, using VisualVM, I was able to find a single method called a very high number of times during the specific usage I was making of the library. After adding the above code segment, it now properly responds to interrupts.

    This is of course not maintainable, plus nothing really guarantees the library will call this method repeatedly in other scenarios; but it worked for me, and since it's relatively easy to profile other applications and insert the checks there, I consider this a generic, if ugly, solution.

    0 讨论(0)
  • 2021-02-05 03:23

    If internal methods have similar names, then you could use pointcut definition in xml (spring/AspectJ) instead of annotations so no code modification of the external library should be needed.

    0 讨论(0)
  • 2021-02-05 03:24

    Like mhaller said, the best option is to launch a new Process. Since your jobs are not that cooperative, you will never have guarantees on the Thread termination.

    A nice solution to your problem would be using a library that supports arbitrary pause/stop of ' lightweight threads' such as Akka instead of the executor service, although this may be a bit of an overkill.

    Although I've never used Akka and cannot confirm it works as you expect, the docs state there's a stop() method for stopping actors.

    0 讨论(0)
  • 2021-02-05 03:26

    To my knowledge there are two ways of using aspect

    • AspecJ
    • Spring AOP

    AspectJ is a customized compiler which intercepts methods that is compiles (meaning it can't get "external methods". Spring AOP (by default) uses class proxying to intercept methods at runtime (so it can intercept "external methods". but the problem with Spring AOP is that it can't proxy already-proxied classes (which AspectJ can do because it does not proxy classes). I think AspectJ could help you in this case.

    0 讨论(0)
  • 2021-02-05 03:27

    The following weird ideas come to my mind:

    • Using a byte code modification library, such as Javassist, to introduce the interrupt-checks at various points within the bytecode. Just at the beginning of methods may not be enough, since you mention that these external methods are not recursive, so you may want to forcefully stop them at any point. Doing this at the byte code level would also make it very responsive, e.g. even if the external code is running within a loop or something, it would be possible to introduce the interrupt checks. However, this will add some overhead, so overall performance will be slower.
    • Launching separate processes (e.g. separate VMs) for the external code. Aborting processes may be much easier to code than the other solutions. The disadvantage is that you would need some sort of communication channel between the external code and your code, e.g. IPC, sockets etc. The second disadvantage is that you need much more resources (CPU, memory) to start up new VMs and it may be environment specific. This would work if you start a couple of tasks using the external code, but not hundreds of tasks. Also, the performance would suffer, but the computation itself would be as fast as the original. Processes can be forcefully stopped by using java.lang.Process.destroy()
    • Using a custom SecurityManager, which performs the interrupt check on each of the checkXXX methods. If the external code somehow calls privileged methods, it may be sufficient for you to abort at these locations. An example would be java.lang.SecurityManager.checkPropertyAccess(String) if the external code periodically reads a system property.
    0 讨论(0)
  • 2021-02-05 03:30

    This solution isn't easy either, but it could work: Using Javassist or CGLIB, you can insert code at the beginning of each internal method (the ones presumably being called by the main run() method) to check if the thread is alive, or some other flag (if it's some other flag, you'll have to add it as well, along with a method to set it).

    I'm proposing Javassist/CGLIB instead of extending the class through code because you mention it's external and you don't want to change the source code, and it may change in the future. So adding the interrupt checks at runtime will work for the current version and also in future versions even if the internal method names change (or their parameters, return values, etc). You just have to take the class and add interrupt checks at the beginning of each method that is not the run() method.

    0 讨论(0)
提交回复
热议问题