8

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.

  • https://stackoverflow.com/questions/26213615/terminating-thread-using-thread-id-in-java check this – xMilos Nov 02 '18 at 13:10
  • volatile boolean stopRequested = false; @Override public void run() { // Thread code while (stopRequested) { // Perform any tidy up activities and return from the run method return; } } public void stop() { stopRequested = true; } – Sorin Penteleiciuc Nov 02 '18 at 13:16
  • 2
    @SorinPenteleiciuc The OP can't alter the code being run and doesn't trust it to behave correctly. – Peter Lawrey Nov 02 '18 at 13:17
  • 1
    Maybe the overall design and architecture *should* be changed instead. So maybe the best solution would be to go a different way, much like a *XY problem*. But this is up to you to decide. – Zabuzard Nov 02 '18 at 14:41
  • @Zabuza well, I've omitted some details that I didn't think were really relevant, such as I didn't really write this initially. I'm trying to modify it for better behavior, because right now the way that this condition is dealt with is for the app to register a task to have it restarted in 5 seconds, and then it kills itself. – You'reAGitForNotUsingGit Nov 02 '18 at 14:45
  • 1
    @AndroidDev You're painted into a corner as there is no method that will have predictable results since you have no idea what the thread is doing. Restarting is probably the best bet if you can't fix the design. Fixing the design requires fully separating what must not break (the app? the ui?) from what can break (the possibly bogus code?) with a proper barrier. – David Schwartz Nov 02 '18 at 16:54

4 Answers4

4

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.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • 1
    @keuleJ The OP, can if the thread is in its own process, and you kill the process. – Peter Lawrey Nov 02 '18 at 13:16
  • 3
    @keuleJ, It is not safe to kill a single thread within a process. Period. If you want to run untrusted, foreign code, then running it in a separate process is the only way you can be able to safely kill it if it goes off the rails. – Solomon Slow Nov 02 '18 at 13:17
  • Hmm, this seems promising, but I'm not sure it will work for my use case; I've added some more details at the bottom of my question. – You'reAGitForNotUsingGit Nov 02 '18 at 14:36
  • @AndroidDev - You may have to modify your base template class (and your main program) to rely on interprocess communication to handle all the interaction with the user, but other than that, why would you think this approach would not work? – Ted Hopp Nov 02 '18 at 14:38
  • @TedHopp well, is it possible to start a class inside the application in a new process? This answer shows starting an entire JAR. Also, how would anything I can do to the base class cause the `runUserProgram()` method (that I have no control over) to exit? – You'reAGitForNotUsingGit Nov 02 '18 at 14:49
  • 1
    @AndroidDev - You can fire up a new process that starts by running code you control, which would proceed to load the desired external code and run it. The base class would be responsible for receiving notifications from the main program that the user desires the external code to stop (if that's something you need to support) and for notifying the main program when a well-behaved external jar finishes (so the UI can be updated). Just as before, of course, your base class still cannot force the external code to wrap things up. But for that, the main program now has `kill -9` available. – Ted Hopp Nov 02 '18 at 14:55
  • @TedHopp ahhhhh I see. I'll try some things in a test project in IntelliJ and get back to you. – You'reAGitForNotUsingGit Nov 02 '18 at 15:23
  • @TedHopp hmm so I see how this could work, but I have a huge concern right now: the two processes need to be able to share objects. Is that possible? – You'reAGitForNotUsingGit Nov 02 '18 at 17:15
  • Actually on second thought, the code could *probably* be refactored so that the objects don't need to be shared... however... the user process needs to be able to access a couple UI elements, so that might still be a problem.. – You'reAGitForNotUsingGit Nov 02 '18 at 17:16
  • @AndroidDev You could run the extra process as a service, where it sends information back for your program to render. i.e. you feed it information and it responds and/or feeds you events. Your core program handles the UI. You could create a basic TCP protocol over loopback. – Peter Lawrey Nov 03 '18 at 04:31
  • @PeterLawrey hmm yeah, but for rendering a camera preview for example, it seems to me that would be extremely inefficient. – You'reAGitForNotUsingGit Nov 03 '18 at 12:00
2

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.

rustyx
  • 80,671
  • 25
  • 200
  • 267
2

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.

Stik
  • 519
  • 4
  • 17
-4

You shold use Thread.interrupt().

keuleJ
  • 3,418
  • 4
  • 30
  • 51