遗憾的是,在
Java中对String使用正则表达式时无法指定超时.因此,如果您没有严格控制哪些模式应用于哪个输入,您可能最终会拥有消耗大量cpu的线程,同时无休止地尝试将(不那么精心设计的)模式与(恶意?)输入匹配.
我知道为什么弃用Thread#stop()的原因(见http://download.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html).它们以可能在ThreadDeath异常情况下被损坏的对象为中心,然后污染正在运行的JVM环境并可能导致细微的错误.
我对这个对JVM工作有更深入了解的人的问题是:如果需要停止的线程没有任何(明显的)监视器或对程序其余部分使用的对象的引用,那么可以使用Thread#stop()吗?
我创建了一个相当防御的解决方案,能够处理与超时匹配的正则表达式.我会很高兴任何评论或评论,尤其是尽管我努力避免它们,这种方法可能导致的问题.
谢谢!
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指出我this solution我已经能够解决我原来的问题,而无需额外的线程.我在那里发布了代码.感谢所有回复的人.