Можно ли в Java использовать Thread #stop ()для уничтожения запущенного потока?

К сожалению, нет возможности указать время ожидания при использовании регулярного выражения для строки в Java. Таким образом, если у вас нет строгого контроля над тем, какие шаблоны применяются к какому входу, вы можете получить потоки, которые потребляют много ресурсов ЦП, бесконечно пытаясь сопоставить (не очень хорошо разработанные )шаблоны с (вредоносными? )ввод.

Мне известны причины, по которым Thread #stop ()устарел (, см.http://download.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html). Они сосредоточены вокруг объектов, которые могут быть повреждены в случае исключений ThreadDeath и которые затем загрязняют вашу работающую среду JVM и могут привести к незаметным ошибкам.

Мой вопрос ко всем, кто глубже меня разбирается в работе JVM, заключается в следующем:Если поток, который необходимо остановить, не содержит никаких (очевидных )мониторов или ссылок на объекты, которые используются остальной частью программы, можно ли тогда использовать поток #остановить ()тем не менее?

Я создал довольно защитное решение, чтобы иметь возможность обрабатывать сопоставление регулярных выражений с тайм-аутом.Я был бы рад любому комментарию или замечанию, особенно в отношении проблем, которые может вызвать этот подход, несмотря на мои попытки их избежать.

Спасибо!

import java.util.concurrent.Callable;

public class SafeRegularExpressionMatcher {

    // demonstrates behavior for regular expression running into catastrophic backtracking for given input
    public static void main(String[] args) {
        SafeRegularExpressionMatcher matcher = new SafeRegularExpressionMatcher(
                "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "(x+x+)+y", 2000);
        System.out.println(matcher.matches());
    }

    final String stringToMatch;

    final String regularExpression;

    final int timeoutMillis;

    public SafeRegularExpressionMatcher(String stringToMatch, String regularExpression, int timeoutMillis) {
        this.stringToMatch = stringToMatch;
        this.regularExpression = regularExpression;
        this.timeoutMillis = timeoutMillis;
    }

    public Boolean matches() {
        CallableThread thread = createSafeRegularExpressionMatchingThread();
        Boolean result = tryToGetResultFromThreadWithTimeout(thread);
        return result;
    }

    private CallableThread createSafeRegularExpressionMatchingThread() {
        final String stringToMatchForUseInThread = new String(stringToMatch);
        final String regularExpressionForUseInThread = new String(regularExpression);
        Callable callable = createRegularExpressionMatchingCallable(stringToMatchForUseInThread,
                regularExpressionForUseInThread);
        CallableThread thread = new CallableThread(callable);
        return thread;
    }

    private Callable createRegularExpressionMatchingCallable(final String stringToMatchForUseInThread,
            final String regularExpressionForUseInThread) {
        Callable callable = new Callable() {
            public Boolean call() throws Exception {
                return Boolean.valueOf(stringToMatchForUseInThread.matches(regularExpressionForUseInThread));
            }
        };
        return callable;
    }

    private Boolean tryToGetResultFromThreadWithTimeout(CallableThread thread) {
        startThreadAndApplyTimeout(thread);
        Boolean result = processThreadResult(thread);
        return result;
    }

    private void startThreadAndApplyTimeout(CallableThread thread) {
        thread.start();
        try {
            thread.join(timeoutMillis);
        } catch (InterruptedException e) {
            throwRuntimeException("Interrupt", e);
        }
    }

    private Boolean processThreadResult(CallableThread thread) {
        Boolean result = null;
        if (thread.isAlive()) {
            killThread(thread); // do not use anything from the thread anymore, objects may be damaged!
            throwRuntimeException("Timeout", null);
        } else {
            Exception exceptionOccurredInThread = thread.getException();
            if (exceptionOccurredInThread != null) {
                throwRuntimeException("Exception", exceptionOccurredInThread);
            } else {
                result = thread.getResult();
            }
        }
        return result;
    }

    private void throwRuntimeException(String situation, Exception e) {
        throw new RuntimeException(situation + " occured while applying pattern /" + regularExpression + "/ to input '"
                + stringToMatch + " after " + timeoutMillis + "ms!", e);
    }

    /**
     * This method uses {@link Thread#stop()} to kill a thread that is running wild. Although it is acknowledged that
     * {@link Thread#stop()} is inherently unsafe, the assumption is that the thread to kill does not hold any monitors on or
     * even references to objects referenced by the rest of the JVM, so it is acceptable to do this.
     * 
     * After calling this method nothing from the thread should be used anymore!
     * 
     * @param thread Thread to stop
     */
    @SuppressWarnings("deprecation")
    private static void killThread(CallableThread thread) {
        thread.stop();
    }

    private static class CallableThread extends Thread {

        private final Callable callable;

        private V result = null;

        private Exception exception = null;

        public CallableThread(Callable callable) {
            this.callable = callable;
        }

        @Override
        public void run() {
            try {
                V result = compute();
                setResult(result);
            } catch (Exception e) {
                exception = e;
            } catch (ThreadDeath e) {
                cleanup();
            }
        }

        private V compute() throws Exception {
            return callable.call();
        }

        private synchronized void cleanup() {
            result = null;
        }

        private synchronized void setResult(V result) {
            this.result = result;
        }

        public synchronized V getResult() {
            return result;
        }

        public synchronized Exception getException() {
            return exception;
        }

    }

}

РЕДАКТИРОВАТЬ:

Благодаря dawce, который указал мне на это решение , я смог решить свою первоначальную проблему без необходимости в дополнительных потоках. Я разместил код там. Спасибо всем, кто ответил.

6
задан Community 23 May 2017 в 12:09
поделиться