我有一个函数,它使用Pattern#compile
和一个Matcher
来搜索字符串列表中的模式。
这个函数被多个线程使用。每个线程在创建时都会传递一个唯一的模式给Pattern#compile
。线程数和模式数是动态的,也就是说我可以在配置期间添加更多的Pattern
和线程。
如果这个函数使用正则表达式,我需要在这个函数上放置synchronize
吗?Java中的正则表达式是线程安全的吗?
我有一个函数,它使用Pattern#compile
和一个Matcher
来搜索字符串列表中的模式。
这个函数被多个线程使用。每个线程在创建时都会传递一个唯一的模式给Pattern#compile
。线程数和模式数是动态的,也就是说我可以在配置期间添加更多的Pattern
和线程。
如果这个函数使用正则表达式,我需要在这个函数上放置synchronize
吗?Java中的正则表达式是线程安全的吗?
是的,来自于Java API文档中的Pattern类
此类(Pattern)的实例是不可变的,且可以安全地供多个并发线程使用。Matcher类的实例则不适合这样的用法。
如果您正在查看性能为中心的代码,请尝试使用reset()方法重置Matcher实例,而不是创建新实例。这将重置Matcher实例的状态,使其可以用于下一个正则表达式操作。实际上,是Matcher实例中维护的状态导致了它不适合进行并发访问。
摘要:
Java正则表达式API被设计成允许一个编译好的模式在多个匹配操作中共享使用。
您可以在不同的线程上安全地调用 Pattern.matcher() 并同时使用matcher对象。 Pattern.matcher() 可以安全地构造无需同步的matcher对象。虽然该方法本身并没有同步,但在Pattern类内部,构造完一个pattern后总是会设置维护一个名为compiled的volatile变量,而在每次调用 matcher() 前该变量都会被读取。这样就可以确保任何引用Pattern对象的线程都可以正确“看到”该对象的内容。
另一方面,您不应该在不同的线程之间共享Matcher对象。或者至少,如果确实需要这样做,那么就应该使用显式的同步机制。
Matcher.java
的代码,可以看到一堆成员变量,包括正在匹配的文本、用于分组的数组、一些维护位置的索引以及一些用于其他状态的boolean
。这都指向了一个有状态的Matcher
,如果被多个Threads
访问,它将无法正常工作。因此,JavaDoc中也提到:
该类的实例不适合在多个并发线程中使用。
只有在您特意允许在单独的Thread
s中使用Matcher
时,才会出现这个问题,就像@Bob Cross指出的那样。如果您需要这样做,并且认为同步对您的代码是一个问题,您可以使用ThreadLocal
存储对象来维护每个工作线程的Matcher
。
虽然您需要记住线程安全需要考虑周围的代码,但您似乎很幸运。Matchers是使用Pattern的matcher工厂方法创建且缺少公共构造函数的事实是一个积极的迹象。同样,您可以使用compile静态方法来创建包含的Pattern。
因此,简而言之,如果您像示例一样执行某些操作:
Pattern p = Pattern.compile("a*b");
Matcher m = p.matcher("aaaaab");
boolean b = m.matches();
你应该做得很不错。
为了澄清代码示例,需要注意的是:这个示例强烈暗示了与 Pattern 和 test 一起创建的 Matcher 是线程本地的。也就是说,不应该将这个 Matcher 暴露给其他线程。
坦白说,这就是任何线程安全问题的风险所在。现实情况是,如果你足够努力,任何代码都可能变得线程不安全。幸运的是,有wonderful books能够教我们许多我们可能毁掉自己代码的方法。只要远离这些错误,我们就大大减少了自己的线程问题概率。
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();
}
}
static {}
?您可以将变量初始化内联并将 Pattern
设置为 final
。 - TWiStErRobprivate static final Pattern emailPattern = Pattern.compile(EMAIL_PATTERN);
更好。 - Christophe Roussy
compile()
方法可能不是。多年来已经发现了两到三个导致在多线程环境下编译失败的 bug。我建议在同步块中执行编译操作。 - Alan Moore