它可以接受在Java中使用线程#停止()来杀掉正在运行的野生线程?(Can it be accept

2019-09-19 01:02发布

遗憾的是没有办法在Java中的字符串使用正则表达式时指定超时。 所以,如果你有在什么模式得到应用到输入没有严格的控制,你可能最终不得不消耗大量的CPU,而无休止地试图匹配(不那么精心设计的)图案(恶意?)输入线程。

我所知道的,为什么线程#停止()被弃用(见的原因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<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谁向我指出这种解决方案我已经能够解决我原来的问题,而不需要额外的线程。 我已经发布的代码存在。 感谢所有谁已作出回应。

Answer 1:

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

注:一个线程可以捕获和忽略ThreadDeath所以停止不保证的广告停止所有线程。

另一种方式来阻止一个线程是在一个不同的进程中运行它。 这可以根据需要被杀死。 这仍然可以让资源在incosistent状态(如锁文件),但它不太可能,更容易控制。

当然,最好的解决方法是修复代码,以便它不这样做摆在首位,并尊重了Thread.interrupt()来代替。



Answer 2:

如果需要线程停止不持有任何(明显)监视器或引用由该程序的其余部分使用的对象,可以在随后接受并使用线程#停止()还是?

它是由你来决定,如果它是“可接受的”。 我们所能做的是就它是否是安全的。 得到的答复是,事实并非如此。

  • 怎么样的非显而易见性监测和引用它持有?

  • 怎么样通知,等它会以其他方式?

  • 怎么样的行动,它可能以其他方式影响静?

问题是,它是困难的(在大多数情况下)肯定知道你已经考虑了所有的线程可能会与应用程序的其余部分可能的相互作用。


重新启动应用程序正是我尽量避免...

这让我感到是你的问题的真正根源; 即你设计一个程序,而不考虑的事实,长期运行的程序都需要重新启动,实际的理由帐户。 尤其是复杂那些有潜在的bug。



Answer 3:

代替使用的Thread.stop()被弃用,使用Thread.interrupt()这将停止提高其可通过检查中断标志isInterrupted()interrupted()或引发InterruptedException

我建立继承Thread类模式是这样的

 class MyThread extends Thread{
      private volatile boolean keepRunning = true;

      public void run(){
           while(keepRunning){
                // do my work
           }
       }

       public void killThread(){
           keepRunning = false;
           this.interrupt();
       }
 }

我不是说我的处理是最完美的方式,也有可能赌更好,但是这对我的作品。



Answer 4:

如果您专门设计你的线程代码,不持有锁等,(是的,这包括非显式的锁。例如,可以改变字符串的大小时,可以使用一个malloc锁),然后停止线程,是的。 轮询一个“打断”标志是好的,不同之处在于它意味着轮询一个“打断”标志,即 它实际上并没有设定时间的99.9999%时的开销。 这可以是具有高性能,紧密的循环的问题。

如果检查可以在最内层循环的被拒之门外,但仍然可以合理地经常检查,那么这的确是最好的一段路要走。

如果该标志不能经常检查,(例如,因为在人迹罕至的库代码一个紧密的循环的),你可以在线程的优先级设置为最低可能忘记它,直到它最终死亡。

即偶尔可能另一个bodge是破坏在其上螺纹以这样的方式使得库代码确实出口正常工作中的数据,会导致抛出异常,并且因此控制泡出的不透明库代码或导致“的OnError “处理程序被调用。 如果库。 在一个字符串操作,用泼洒空字符串是肯定要做的事情。 当你控制回来,如果你可以安排在线程的AV /段错误,那么细,只要 - 任何异常都行。



文章来源: Can it be acceptable in Java to use Thread#stop() to kill a thread that is running wild?