Answers:
是的,来自Pattern类的Java API文档
此类(模式)的实例是不可变的,可以安全地由多个并发线程使用。Matcher类的实例不安全用于此类用途。
如果您正在查看以性能为中心的代码,请尝试使用reset()方法重置Matcher实例,而不是创建新实例。这将重置Matcher实例的状态,使其可用于下一个正则表达式操作。实际上,正是Matcher实例中维护的状态才使它对于并发访问不安全。
摘要:
Java正则表达式API已设计为允许在多个匹配操作之间共享单个编译模式。
您可以从不同的线程以相同的模式安全地调用 Pattern.matcher(),并安全地同时使用匹配器。 Pattern.matcher()可安全地构建不同步的匹配器。尽管该方法不是同步的,但在Pattern类内部,但始终在构造模式后设置一个称为compile的易失变量,并在对matcher()的调用开始时读取该变量。 这会强制所有引用Pattern的线程正确地“查看”该对象的内容。
另一方面,您不应该在不同线程之间共享Matcher。或者至少,如果曾经这样做,则应使用显式同步。
尽管您需要记住线程安全性也必须考虑周围的代码,但是您似乎很幸运。这样的事实匹配器使用模式的创建匹配的工厂方法,缺乏公共的构造函数是一个积极的迹象。同样,您可以使用compile static方法创建包含的Pattern。
简而言之,如果您执行类似示例的操作:
Pattern p = Pattern.compile("a*b");
Matcher m = p.matcher("aaaaab");
boolean b = m.matches();
你应该做得不错。
为了清楚起见,对代码示例进行了后续操作:请注意,此示例强烈暗示由此创建的Matcher在Pattern和测试中是线程局部的。即,您不应将由此创建的Matcher暴露给任何其他线程。
坦白说,这就是任何线程安全问题的风险。现实情况是,如果您努力尝试,任何代码都可能被设置为线程不安全的。幸运的是,有很多很棒的 书,教给我们很多破坏代码的方式。如果我们避免这些错误,那么将大大降低我们自己出现线程问题的可能性。
快速浏览一下的代码,将看到Matcher.java
一堆成员变量,包括要匹配的文本,组的数组,用于维护位置的一些索引以及boolean
用于其他状态的s。所有这些都指向一个有状态的状态Matcher
,如果被多个访问,状态将不会很好Threads
。如此做的JavaDoc:
此类的实例不适用于多个并发线程。
正如@Bob Cross指出的那样,这只是一个问题,如果您竭尽所能允许Matcher
单独使用Thread
s。如果您需要执行此操作,并且认为同步将成为代码问题,那么您可以选择使用ThreadLocal
存储对象来维护Matcher
每个工作线程。
综上所述,您可以重用(保留静态变量)已编译的Pattern,并在需要验证它们针对某些字符串的正则表达式模式时,告诉它们为您提供新的Matchers。
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/(接近尾声)(万一它不适合那些需要通过电子邮件验证的人,因为它在这里发布)
static {}
?您可以内联该变量初始化并进行初始化Pattern
final
。
private static final Pattern emailPattern = Pattern.compile(EMAIL_PATTERN);
更好。
compile()
方法可能不是。这些年来,已经有两到三个错误导致编译在多线程环境中失败。我建议在同步块中进行编译。