如何在运行正则表达式的java函数上设置时间限制

我在java函数中运行正则表达式来解析文档,如果找到正则表达式指定的字符串则返回true,如果没有则返回false。 但问题是,当文档不包含正则表达式指定的字符串时,返回false需要很长时间,如果执行时间超过6秒,我想终止该函数。

如何在该function上设置6秒的时间限制,以便在超过6秒的时间内强制终止该function。

我从类1调用类2的方法“方法1”。“方法1”调用同一类的“方法2”,即“类2”。 方法2在文档上运行正则表达式代码。 如果它找到正则表达式指定的字符串,则它将结果返回给方法1,方法1又将结果返回到“类1”中的方法,该方法调用了类2的“方法1”。现在的问题是类2的method1和method2的执行时间不应超过6秒。

所以,我在同一个文件中创建了一个新的RegexpThread类,其中我的class2是。 然后我将class2的method2移动到RegexpThread类中。 然后每当调用方法1时,它都会实例化RegexpThread类,如下所示:

RegexpThread rt = new RegexpThread() { public void run() { method 2(m, urlCopy, document); } }; rt.start(); try { rt.join(6 * 1000); } catch (InterruptedException e) { return "y"; } if(rt.getResultXml().equals("")) { return "g"; } resultXml.append(rt.getResultXml()); return resultXml.toString(); 

显示的代码位于class2的方法1中。 RegexpThread类中的方法2对文档执行一些正则表达式搜索。 RegexpThread类中有一个名为“resultXml”的私有字段。 如果方法2找到了正则表达式指定的字符串,则它将结果分配给私有字段“resultXml”。 如果没有,则“resultXml”包含其默认值,即空字符串。

因此,在上面的“if block”中,它正在针对空字符串检查“resultXml”字段。 如果它是一个空字符串,那么这意味着正则表达式没有在文档中找到它的字符串。 但是如果它不是空字符串那么这意味着正则表达式在文档中找到了字符串并将结果分配给“resultXml”字段。

所以,看看这个并告诉我该怎么做……

你做了什么看起来很好我在这里是我如何修改它:

 final AtomicReference resultXml = new AtomicReference(); RegexpThread rt = new RegexpThread() { public void run() { method2(m, urlCopy, document, resultXml); } }; rt.start(); try { rt.join(6 * 1000); } catch (InterruptedException e) { return "y"; } if(resultXml.get() == null) { rt.interupt(); return "g"; } resultXml.append(resultXml.get()); return resultXml.toString(); 

我可能会在这里弄错,但我认为终止一个线程的所有方法都已被弃用了一段时间 。 建议的方法是使用工作线程定期检查并在设置时正常退出的共享isRunning变量。

这对你的情况不起作用,但在我看来你正在治疗症状 – 而不是真正的问题。 你应该发布你的regexp函数的代码,需要6秒才能执行。 如果它是re​​gexp本身,则执行时间可能是灾难性回溯的情况。

我现在假设您的正则表达式代码是正确的,并且它确实是一些计算机代码,它受CPU限制为6s。

鉴于上述情况,我认为你只有一个选择。 在多个阶段/迭代中执行代码并检查变量以停止请求。 您不能使用普通的Pattern / Matcher代码执行此操作。

您可以通过以某种方式预先拆分输入字符串,然后逐位输入正则表达式(您的初始拆分必须独立于正则表达式)来完成此操作。

不能这样做:

  1. 使用Thread.stop()等。这已被弃用,无法正常工作。
  2. 使用Thread.interrupt() 。 这会在线程上设置一个中断标志,仅在线程执行IO时进行检查。 如果线程是CPU绑定的,那么永远不会检查该标志。

鉴于上述情况,我将再次看看为什么正则表达式需要6s才能匹配。 正则表达式是否正确? 你可以在较小的文本段上执行正则表达式吗?

有两种方法可以回答这个问题。

一方面,没有任何实用/有效的方法可以安全地杀死正在执行Matcher.find(...)Matcher.match(...)的线程。 调用Thread.stop()会起作用,但存在重大的安全问题。 解决这个问题的唯一方法是开发自己的正则表达式引擎,定期检查interrupted标志。 (这不是完全不切实际的。例如,如果GPL不是您的问题,您可以从OpenJDK中的现有正则表达式引擎开始。)

另一方面,您的问题的真正根源(很可能)是您以错误的方式使用正则表达式。 你要么想要为一个正则表达式做一些太复杂的事情,要么你的正则表达式是次优的。

编辑 :正则表达式花费太长时间的典型原因是多个量词(? ,, +)导致病态回溯。 例如,如果您尝试匹配一串N“A”字符后跟“B”与正则表达式“^ A A A A A A $”,则计算的复杂度(至少)为O(N *) * 5)。 这是一个更“真实世界”的例子:

 "(.*)(.*)(.*)(.*)(.*)(.*)(.*)" 

现在想象如果你遇到这样的“网页”会发生什么:

      

请注意,没有关闭标记。 这将在失败前运行很长时间。 (我不确定复杂性是什么……但你可以通过实验估计它你觉得它。)

在这种情况下,一个简单的答案是使用更简单的正则表达式来定位6个标记元素,然后使用substring()在之间提取东西。

通过ExecutorService启动你的线程并给它一个超时,如下所示:

 ExecutorService pool = Executors.newFixedThreadPool(POOL_SIZE); pool.execute(rt); pool.awaitTermination(timeout, timeUnit); 

awaitTermination()将等到任务完成(以及此ExecutorService下的所有其他任务),线程被中断或发生超时 – 这是第一次出现的。

这听起来很符合您的需求。

您没有显示实际执行正则表达式的函数,因此我假设它从文件中读取行并在每行上执行正则表达式。

如果是这种情况,那么更好的解决方案是将超时值传递给该函数。 在每N行(无论N可能是什么)之后,它会检查超时值。

您将遇到的真正问题是阻止IO – 例如,从网络读取。 在这种情况下,你无法用Java做任何事情,因为这个块实际上是在OS内核中发生的。

Java Thread类没有配备来处理这种中断,因此不适合您的要求。

我将使用ProcessBuilder在单独的Process实现该function,并使用Process类提供的Input和Output流进行通信。 Process类的destroy方法提供了强制中断。

我相信这是您所需要的正确,最安全的实施方案。 遗憾的是,Java并不容易以独立于平台的方式启动另一个Java进程,因此您必须将java可执行文件放到路径中并创建一个单独的main方法来执行此操作。 这比应该的更难。

我同意在使用前检查正则表达式。 如果你需要一个安全网,你可能会使用这样的东西……

http://gist.github.com/630969

您可以使用来自jcabi-aspects的 AOP和@Timeable注释(我是开发人员):

 @Timeable(limit = 1, unit = TimeUnit.SECONDS) String yourMethod() { // execution as usual } 

确保您在方法中的某个位置检查Thread#isInterrupted()

 if (Thread.currentThread.isInterrupted()) { throw new IllegalStateException("time out"); } 

当达到时间限制时,您的线程将isInterrupted()标志设置为true ,并且您的工作是正确处理这种情况并停止执行。