Are Thread.stop and friends ever safe in Java?

前端 未结 8 1124
清酒与你
清酒与你 2020-11-28 08:05

The stop(), suspend(), and resume() in java.lang.Thread are deprecated because they are unsafe. The Oracle recommended w

相关标签:
8条回答
  • 2020-11-28 08:22

    Maybe there's something I don't know, but as java.sun.com said, it is unsafe because anything this thread is handling is in serious risk to be damaged. Other objects, connections, opened files... for obvious reasons, like "don't shut down your Word without saving first".

    For this find(...) exemple, I don't really think it would be a catastrophe to simply kick it away with a sutiless .stop()...

    0 讨论(0)
  • 2020-11-28 08:23

    Here's my attempt at answering my own question.

    I think that the following conditions should be sufficient for a single thread to be safely stopped using Thread.stop():

    1. The thread execution must not create or mutate any state (i.e. Java objects, class variables, external resources) that might be visible to other threads in the event that the thread is stopped.
    2. The thread execution must not use notify to any other thread during its normal execution.
    3. The thread must not start or join other threads, or interact with then using stop, suspend or resume.

    (The term thread execution above covers all application-level code and all library code that is executed by the thread.)

    The first condition means that a stopped thread will not leave any external data structures or resources in an inconsistent state. This includes data structures that it might be accessing (reading) within a mutex. The second condition means that a stoppable thread cannot leave some other thread waiting. But it also forbids use of any synchronization mechanism other that simple object mutexes.

    A stoppable thread must have a way to deliver the results of each computation to the controlling thread. These results are created / mutated by the stoppable thread, so we simply need to ensure that they are not visible following a thread stop. For example, the results could be assigned to private members of the Thread object and "guarded" with a flag that is atomically by the thread to say it is "done".

    EDIT: These conditions are pretty restrictive. For example, for a "regex evaluator" thread to be safely stopped, if we must guarantee that the regex engine does not mutate any externally visible state. The problem is that it might do, depending on how you implement the thread!

    1. The Pattern.compile(...) methods might update a static cache of compiled patterns, and if they did they would (should) use a mutex to do it. (Actually, the OpenJDK 6.0 version doesn't cache Patterns, but Sun might conceivably change this.)
    2. If you try to avoid 1) by compiling the regex in the control thread and supplying a pre-instantiated Matcher, then the regex thread does mutate externally visible state.

    In the first case, we would probably be in trouble. For example, suppose that a HashMap was used to implement the cache and that the thread was interrupted while the HashMap was being reorganized.

    In the second case, we would be OK provided that the Matcher had not been passed to some other thread, and provided that the controller thread didn't try to use the Matcher after stopping the regex matcher thread.

    So where does this leave us?

    Well, I think I have identified conditions under which threads are theoretically safe to stop. I also think that it is theoretically possible to statically analyse the code of a thread (and the methods it calls) to see if these conditions will always hold. But, I'm not sure if this is really practical.

    Does this make sense? Have I missed something?

    EDIT 2

    Things get a bit more hairy when you consider that the code that we might be trying to kill could be untrusted:

    1. We can't rely on "promises"; e.g. annotations on the untrusted code that it is either killable, or not killable.

    2. We actually need to be able to stop the untrusted code from doing things that would make it unkillable ... according to the identified criteria.

    I suspect that this would entail modifying JVM behaviour (e.g. implementing runtime restrictions what threads are allowed to lock or modify), or a full implementation of the Isolates JSR. That's beyond the scope of what I was considering as "fair game".

    So lets rule the untrusted code case out for now. Or at least, acknowledge that malicious code can do things to render itself not safely killable, and put that problem to one side.

    0 讨论(0)
  • 2020-11-28 08:27

    A concrete example would probably help here. If anyone can suggest a good alternative to the following use of stop I'd be very interested. Re-writing java.util.regex to support interruption doesn't count.

    import java.util.regex.*;
    import java.util.*;
    
    public class RegexInterruptTest {
    
        private static class BadRegexException extends RuntimeException { }
            final Thread mainThread = Thread.currentThread();
            TimerTask interruptTask = new TimerTask() {
                public void run() {
                    System.out.println("Stopping thread.");
                    // Doesn't work:
                    // mainThread.interrupt();
                    // Does work but is deprecated and nasty
                    mainThread.stop(new BadRegexException());
                }
            };
    
            Timer interruptTimer = new Timer(true);
            interruptTimer.schedule(interruptTask, 2000L);
    
            String s = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab";
            String exp = "(a+a+){1,100}";
            Pattern p = Pattern.compile(exp);
            Matcher m = p.matcher(s);
            try {
                System.out.println("Match: " + m.matches());
                interruptTimer.cancel();
            } catch(BadRegexException bre) {
                System.out.println("Oooops");
            } finally {
                System.out.println("All over");
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-28 08:27

    There are ways to use Thread.stop() relatively stable w/o leaking memory or file descriptors (FDs are exceptionally leak prone on *NIX) but you shall rely on it only if you are forced to manage 3rd party code. Never do use it to achieve the result if you can have control over the code itself.

    If I use Thread.stop along w/ interrupt() and some more hacks stuff like adding custom logging handlers to re-throw the trapped ThreadDeath, adding unhandleExceltionHandler, running into your own ThreadGroup (sync over 'em), etc...

    But that deserves an entire new topic.

    But in this case it's the Java Designers telling you; and they're more authorative on their language then either of us :)

    Just a note: quite a few of them are pretty clueless

    0 讨论(0)
  • 2020-11-28 08:35

    If my understanding is right, the problem has to do with synchronization locks not being released as the generated ThreadInterruptedException() propagates up the stack.

    Taking that for granted, it's inherently unsafe because you can never know whether or not any "inner method call" you happened to be in at the very moment stop() was invoked and effectuated, was effectively holding some synchronization lock, and then what the java engineers say is, seemingly, unequivocally right.

    What I personally don't understand is why it should be impossible to release any synchronization lock as this particular type of Exception propagates up the stack, thereby passing all the '}' method/synchronization block delimiters, which do cause any locks to be release for any other type of exception.

    I have a server written in java, and if the administrator of that service wants a "cold shutdown", then it is simply NECESSARY to be able to stop all running activity no matter what. Consistency of any object's state is not a concern because all I'm trying to do is to EXIT. As fast as I can.

    0 讨论(0)
  • 2020-11-28 08:36

    All forms of concurrency control can be provided by the Java synchronization primitives by constructing more complex concurrency controls that suit your problem.

    The reasons for deprecation are clearly given in the link you provide. If you're willing to accept the reasons why, then feel free to use those features.

    However, if you choose to use those features, you also accept that support for those features could stop at any time.

    Edit: I'll reiterate the reason for deprecation as well as how to avoid them.

    Since the only danger is that objects that can be referenced by the stoped thread could be corrupted, simply clone the String before you pass it to the Thread. If no shared objects exist, the threat of corrupted objects in the program outside the stoped Thread is no longer there.

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