Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forcefully terminating a thread I didn't write in Java

Everywhere I look about how to forcefully stop a thread in Java, I see "just do an exit variable check instead, your program is broken if you need to force kill."

I have a rather unique situation though. I am writing a Java program that dynamically loads and runs other Java classes in a separate thread. (No comments about security risks please, this is a very specific use case).

The trouble is, since other people will have written the classes that need to be loaded, there's no way to guarantee they'll implement the stop checking and whatnot correctly. I need a way to immediately terminate their thread, accepting all the risks involved. Basically I want to kill -9 their thread if I need to. How can I do this in Java?


Update: here's a bit more info:

  • This is actually an Android app

  • The user code depends on classes in my application

  • A user class must be annotated with @UserProgram in order to be "registered" by my application

  • The user also has the option of building their classes right into the application (by downloading a project with the internal classes already compiled into a libraries and putting their classes in a separate module) rather than having them dynamically loaded from a JAR.

  • The user classes extend from my template class which has a runUserProgram() method that they override. Inside that method, they are free to do anything they want. They can check isStopRequested() to see if I want them to stop, but I have no guarantee that they'll do that.

  • On startup, my application loads any JARs specified and scans both all the classes in the application and the classes in those JARs to find any classes annotated with the aforementioned annotation. Once a list of those classes is built, it is fed into the frontend where the UI provides a list of programs that can be run. Once a program is selected, a "start" button must be pressed to actually start it. When it is pressed, the button changes to a "stop" button and a callback is fired into the backend to load up the selected class in a new thread and call the runUserProgram() method. When the "stop" button is pressed, a variable is set which causes isStopRequested() to return true.

like image 448
You'reAGitForNotUsingGit Avatar asked Nov 02 '18 13:11

You'reAGitForNotUsingGit


3 Answers

You can kill -9 it by running in its own process i.e. start with a ProcessBuilder and call Process.destroyForcibly() to kill it.

    ProcessBuilder pb = new ProcessBuilder("java", "-cp", "myjar.jar");
    pb.redirectErrorStream();
    Process process = pb.start();
    // do something with the program.
    Scanner sc = new Scanner(process.getOutputStream());
    while (sc.hasNextLine()) {
        System.out.println(sc.nextLine());
    }
    // when done, possibly in another thread so it doesn't get blocked by reading.
    process.waitFor(1, TimeUnit.SECONDS);
    if (process.isAlive())
        process.destroyForcibly();

Java 8 had Thread.stop(). The problem is that it could only work reasonably for very limited use cases, so limited you were better off using interrupts, and if the code isn't trusted, neither are any good.

like image 125
Peter Lawrey Avatar answered Sep 28 '22 18:09

Peter Lawrey


There is the deprecated Thread.stop() but don't use it.

There is no way to cleanly terminate another thread without it cooperating.

The thread can be in a state where it allocated some memory, or added some objects to some global state, locked some mutexes, etc. If you kill it at the wrong moment, you risk leaking memory or even causing a deadlock.

like image 32
rustyx Avatar answered Sep 28 '22 19:09

rustyx


It would be possible through JNI, under Windows there is a TerminateThread API that you can call, there is (hopefully) probably a similar thing under Android. The trouble will be getting the thread's native handle, you would need to obtain that when your user "program" is first loaded, probably by calling another JNI method from the thread in question as part of the initialisation process and getting the current thread handle from that.

I have not tried this myself, best case is that this "works" and kills the thread, but it is going to cause that thread to leak resources. Worst case is that it will leave the JVM in an inconsistent state internally, which will probably crash your entire application.

I really think this is a Bad Idea.

A better design, if you want to allow this, is to run your user code in another process and communicate with it via sockets or pipes. This way you can relatively safely terminate the other process if necessary. It's more work, but it's going to be a lot better in the long run.

like image 33
Stik Avatar answered Sep 28 '22 18:09

Stik