К сожалению, нет возможности указать время ожидания при использовании регулярного выражения для строки в 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, который указал мне на это решение , я смог решить свою первоначальную проблему без необходимости в дополнительных потоках. Я разместил код там. Спасибо всем, кто ответил.