在 Java 中使用 Thread#stop() 杀死正在运行的线程是否可以接受? [英] Can it be acceptable in Java to use Thread#stop() to kill a thread that is running wild?

查看:47
本文介绍了在 Java 中使用 Thread#stop() 杀死正在运行的线程是否可以接受?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

遗憾的是,在 Java 中对 String 使用正则表达式时,无法指定超时.因此,如果您无法严格控制将哪些模式应用于哪些输入,那么您最终可能会拥有消耗大量 CPU 的线程,同时无休止地尝试将(设计不佳的)模式与(恶意的?)输入进行匹配.

Regrettably there is no way to specify a timeout when using a regular expression on a String in Java. So if you have no strict control over what patterns get applied to which input, you might end up having threads that consume a lot of CPU while endlessly trying to match (not so well designed) patterns to (malicious?) input.

我知道 Thread#stop() 被弃用的原因(参见 http://download.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html).它们以在出现 ThreadDeath 异常时可能会损坏的对象为中心,然后会污染正在运行的 JVM 环境并可能导致细微错误.

I'm aware of the reasons why Thread#stop() is deprecated (see http://download.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html). They are centered around objects that might get damaged in case of ThreadDeath exceptions, and which then pollute your running JVM environment and can lead to subtle errors.

我对任何比我对 JVM 的工作有更深入了解的人的问题是:如果需要停止的线程没有持有任何(明显的)监视器或对被 JVM 使用的对象的引用程序的其余部分,仍然可以使用 Thread#stop() 吗?

My question to anyone who has deeper insight than me into the workings of the JVM is this: If the thread that needs to be stopped does not hold any (obvious) monitors on or references to objects that are used by the rest of the program, can it then be acceptable to use Thread#stop() nevertheless?

我创建了一个相当防御性的解决方案,以便能够处理与超时匹配的正则表达式.我很高兴收到任何评论或评论,尤其是尽管我努力避免这些方法可能导致的问题.

I created a rather defensive solution to be able to process regular expression matching with a timeout. I would be glad for any comment or remark, especially on problems that this approach can cause despite my efforts to avoid them.

谢谢!

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<Boolean> thread = createSafeRegularExpressionMatchingThread();
        Boolean result = tryToGetResultFromThreadWithTimeout(thread);
        return result;
    }

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

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

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

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

    private Boolean processThreadResult(CallableThread<Boolean> 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<Boolean> thread) {
        thread.stop();
    }

    private static class CallableThread<V> extends Thread {

        private final Callable<V> callable;

        private V result = null;

        private Exception exception = null;

        public CallableThread(Callable<V> 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 向我指出了这个解决方案我已经能够在不需要额外线程的情况下解决我原来的问题.我已经在那里发布了代码.感谢所有回复的人.

Thanks to dawce who pointed me to this solution I have been able to solve my original problem without the need for additional threads. I have posted the code there. Thanks to all who have responded.

推荐答案

如果您确定它是唯一可用的解决方案,您可以使用 Thread.stop().您可能需要关闭并重新启动应用程序以确保其处于良好状态.

You can use Thread.stop() if you determine its the only solution available to you. You may need to shutdown and restart your applicaton to ensure its in a good state.

注意:线程可以捕获并忽略ThreadDeath,因此停止并不能保证停止所有线程.

Note: a Thread can capture and ignore ThreadDeath so stop isn't guarenteed to stop all threads.

停止线程的另一种方法是在不同的进程中运行它.这可以根据需要杀死.这仍然会使资源处于不一致状态(如锁定文件),但不太可能且更容易控制.

An alternative way to stop a thread is to run it in a different process. This can be killed as required. This can still leave resources in an incosistent state (like lock files) but it is less likely and easier to control.

当然最好的解决方案是修复代码,这样它就不会首先这样做,而是尊重 Thread.interrupt().

The best solution of course is to fix the code so it doesn't do this in the first place and respects Thread.interrupt() instead.

这篇关于在 Java 中使用 Thread#stop() 杀死正在运行的线程是否可以接受?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆