正则表达式线程安全吗?

我有一个使用 Pattern#compileMatcher的函数来搜索字符串列表中的模式。

此函数用于多个线程。在创建线程时,每个线程都有一个传递给 Pattern#compile的唯一模式。线程和模式的数量是动态的,这意味着我可以在配置期间添加更多的 Pattern和线程。

如果这个函数使用正则表达式,我是否需要在它上面放一个 synchronize?在 Java 中使用正则表达式是否安全?

57069 次浏览

虽然您需要记住线程安全性也必须考虑到周围的代码,但是您似乎很幸运。火柴是使用模式的 matcher工厂方法创建的,并且缺少公共构造函数,这是一个积极的信号。同样,您可以使用 编译静态方法来创建包含 模式的。

所以,简而言之,如果你做一些类似的例子:

Pattern p = Pattern.compile("a*b");
Matcher m = p.matcher("aaaaab");
boolean b = m.matches();

你会做得很好的。

为了清晰起见,请跟进代码示例: 注意,这个示例强烈暗示因此创建的 Matcher 与 Pattern 和测试是线程本地的。也就是说,您不应该将这样创建的 Matcher 暴露给任何其他线程。

坦率地说,这是任何线程安全问题的风险。事实上,如果您足够努力,任何代码可能会变得线程不安全。幸运的是,有 太棒了 教我们一大堆方法,我们可以破坏我们的代码。如果我们远离这些错误,我们就大大降低了自己出现线程问题的可能性。

Java 中正则表达式的线程安全性

总结:

Java 正则表达式 API 具有 被设计成允许 要共享的已编译模式 多重匹配操作多重匹配操作。

你可以放心地打电话 在来自不同线程的相同模式上使用 Pattern.matcher () 同时安全地使用火柴。 Pattern.matcher () 可以安全地构造不使用 同步。虽然方法 不是同步的 模式课 Volatile变量 被称为编译的文件总是设置在 构造一个模式并在 开始呼叫 Matcher (). This forces any thread referring to 正确“看到”的模式 物品的内容。

另一方面,你不应该分享 不同线程之间的匹配器。 或者至少,如果你曾经做过,你 应该使用显式同步。

是的 ,从 Java API 文档的 < a href = “ http://docs.oracle.com/javase/7/docs/API/Java/util/regex/Pattern.html”rel = “ noReferrer”> 模式类

这个(模式)类的实例是不可变的,并且对于多个并发线程来说是安全的。使用 Matcher 类的实例不安全。

如果正在查看以性能为中心的代码,请尝试使用 set ()方法重置 Matcher 实例,而不要创建新实例。这将重置 Matcher 实例的状态,使其可用于下一个正则表达式操作。事实上,正是 Matcher 实例中维护的状态使其不能进行并发访问。

快速浏览一下 Matcher.java的代码,可以看到一组成员变量,包括正在匹配的文本、组的数组、用于维护位置的几个索引和用于其他状态的几个 boolean。这一切都指向一个有状态的 Matcher,如果被多个 Threads访问,它的行为将不会很好。JavaDoc也是如此:

此类的实例对于多个并发使用是不安全的 丝线。

正如@Bob Cross 指出的那样,只有当您特意允许在单独的 Thread中使用 Matcher时,这才是一个问题。如果您需要这样做,并且您认为同步对于您的代码来说是一个问题,那么您可以选择使用一个 ThreadLocal存储对象来维护每个工作线程一个 Matcher

总而言之,您可以重用(保留静态变量)已编译的模式,并告诉它们在需要针对某些字符串验证这些正则表达式模式时为您提供新的 Matcher

import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
* Validation helpers
*/
public final class Validators {


private static final String EMAIL_PATTERN = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$";


private static Pattern email_pattern;


static {
email_pattern = Pattern.compile(EMAIL_PATTERN);
}


/**
* Check if e-mail is valid
*/
public static boolean isValidEmail(String email) {
Matcher matcher = email_pattern.matcher(email);
return matcher.matches();
}


}

关于上面用于验证电子邮件的 RegEx 模式,请参见 http://zoomicon.wordpress.com/2012/06/01/validating-e-mails-using-regular-expressions-in-java/(接近末尾)(如果它不符合电子邮件验证的需要,请参见此处)