Java Regexスレッドセーフですか?


104

文字列のリストからパターンを検索するためにPattern#compileおよびを使用する関数がMatcherあります。

この関数は複数のスレッドで使用されます。各スレッドにはPattern#compile、スレッドの作成時にに渡される固有のパターンがあります。スレッドとパターンの数は動的です。つまり、Pattern構成中にとスレッドをさらに追加できます。

synchronize正規表現を使用する場合、この関数にaを付ける必要がありますか?Javaスレッドの正規表現は安全ですか?

回答:


132

はいPatternクラスの Java APIドキュメントから

この(Pattern)クラスのインスタンスは不変であり、複数の同時スレッドで安全に使用できます。Matcherクラスのインスタンスは、そのような使用には安全ではありません。

パフォーマンス中心のコードを見ている場合は、新しいインスタンスを作成する代わりに、reset()メソッドを使用してMatcherインスタンスをリセットしてみてください。これにより、Matcherインスタンスの状態がリセットされ、次の正規表現操作で使用できるようになります。実際、Matcherインスタンスで維持される状態は、同時アクセスに対して安全ではないことを担っています。


17
パターンオブジェクトはスレッドセーフですが、compile()メソッドはそうでない場合があります。マルチスレッド環境でコンパイルが失敗する原因となったバグは、長年にわたって2〜3個ありました。コンパイルを同期ブロックで行うことをお勧めします。
Alan Moore

4
はい、Patternクラスで同時実行バグが発生しているため、同期アクセスについてのアドバイスを歓迎します。ただし、Patternクラスの元の開発者は、Patternクラスをスレッドセーフにすることを意図しており、これはJavaプログラマーが信頼できる契約でなければなりません。率直に言うと、コントラクトでスレッドセーフの動作に依存するよりも、スレッドローカル変数を使用し、パフォーマンスヒットを最小限に抑えたいと思います(コードを見たことがない限り)。彼らは「スレッディングは簡単ですが、正しい同期は難しい」と言っています。
Vineet Reynolds

1
「パターン」のソースは、Oracle JDKディストリビューションに含まれていることに注意してください(oracle.com/technetwork/java/faq-141681.html#A14によると、「Java 2 SDK、Standard Edition自体には、src.zipというファイルが含まれています。 Javaパッケージ内のパブリッククラスのソースコードが含まれています」)ので、簡単に確認できます。
David Tonhofer 2013年

@DavidTonhofer最新のJDKにはバグのない正しいコードが含まれていると思いますが、Javaの中間.classファイルは互換性のあるVMによってどのプラットフォームでも解釈できるため、それらの修正がそのランタイムに存在するかどうかはわかりません。もちろん、ほとんどの場合、サーバーが実行しているバージョンを知っていますが、すべてのバージョンを確認するのは面倒です。
TWiStErRob 2016

12

Javaの正規表現によるスレッドセーフ

概要:

Java正規表現APIは、単一のコンパイル済みパターンを複数の一致操作で共有できるように設計されています。

異なるスレッドから同じパターンでPattern.matcher()を安全に呼び出すことができ 、マッチャーを同時に安全に使用できます。 Pattern.matcher()は、同期せずにマッチャーを構築しても安全です。このメソッドは、Patternクラスの内部で同期化されていませんが、パターンを作成した後は常に、compiledと呼ばれる揮発性変数が設定され、matcher()の呼び出しの開始時に読み取られます。 これにより、パターンを参照するすべてのスレッドは、そのオブジェクトの内容を正しく「見る」ことができます。

一方、異なるスレッド間でマッチャーを共有しないでください。または、少なくとも、使用したことがある場合は、明示的な同期を使用する必要があります。


2
@akf、ところで、これはディスカッションサイト(このサイトとよく似ている)であることに注意してください。ここで見つけた情報よりも良くも悪くもないものは何でも考えます(つまり、James Goslingからの1つの真の言葉ではありません)。
ボブ・クロス

3

スレッドセーフは周囲のコードも考慮する必要があることを覚えておく必要がありますが、幸運なようです。事実の照合プログラムは、パターンの使用して作成されたマッチャーファクトリメソッドを、パブリックコンストラクタを持たないが正の符号です。同様に、静的なコンパイル方法を使用して、包含パターンを作成します。

つまり、簡単に言うと、例のようなことをすると:

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

あなたはかなりうまくいっているはずです。

わかりやすくするためにコード例のフォローアップ:この例は、このように作成されたマッチャーがパターンとテストでスレッドローカルであることを強く示唆していることに注意してください。つまり、作成したマッチャーを他のスレッドに公開しないでください。

率直に言って、それはスレッドセーフの質問のリスクです。実際には、十分な努力をすると、すべてのコードがスレッドセーフにならない可能性があります。幸いなことに、コードを台無しにしてしまう可能性のあるたくさんの方法を教えてくれる素晴らしい があります。これらのミスを避ければ、スレッド化の問題が発生する可能性が大幅に減少します。


@Jason S:スレッドの局所性は、内部コードがスレッドセーフでなくても、スレッドセーフを実現する非常に簡単な方法の1つです。一度に1つのメソッドしか特定のメソッドにアクセスできない可能性がある場合は、外部でスレッドセーフを適用しています。
ボブ・クロス

1
わかりました。つまり、使用時に文字列からパターンを再作成することは、同時実行の問題に対処するリスクがあるため、効率的に格納するよりも優れていると言っているだけですか。私はあなたにそれを許可します。私は、ファクトリーメソッドとパブリックコンストラクターについてのこの文と混同されました。それは、このトピックの赤いニシンのようです。
ジェイソンS

@Jason S、いや、ファクトリメソッドとコンストラクタの欠如は、他のスレッドとの結合の脅威を軽減できる方法の一部です。私のパターンと一致するマッチャーを取得できる唯一の方法がp.matcher()を介する場合、他の誰も私のマッチャーに副作用を及ぼすことはできません。ただし、私自身でも問題が発生する可能性があります。そのMatcherを返すパブリックメソッドがある場合、別のスレッドがそのMatcherを取得して副作用を起こす可能性があります。つまり、同時実行性は困難です(どの言語でも)。
ボブ・クロス

2

のコードをざっと見てみると、Matcher.java一致するテキスト、グループの配列、位置を維持するためのいくつかのインデックスboolean、その他の状態のためのいくつかのs など、メンバー変数の束が示されています。これはすべてMatcher、複数からアクセスされた場合に適切に動作しないステートフルを指しますThreadsJavaDocも同様です。

このクラスのインスタンスは、複数の同時スレッドによる使用には安全ではありません。

これは、@ Bob Crossが指摘しているようにMatcher、別Threadのでの使用を許可するために邪魔になった場合にのみ問題になります。これを行う必要があり、コードの同期が問題になると思われる場合は、ThreadLocalストレージオブジェクトを使用してMatcher作業スレッドごとに維持するオプションがあります。


1

要約すると、コンパイルされたパターンを再利用(静的変数に保持)し、それらの正規表現のパターンを一部の文字列に対して検証する必要があるときに、新しいマッチャーを提供するように指示できます。

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();
  }

}

参照http://zoomicon.wordpress.com/2012/06/01/validating-e-mails-using-regular-expressions-in-java/を(電子メールを検証するために上記で使用正規表現パターンについて(端部近傍)ここに投稿されているため、電子メール検証のニーズに適合しない場合)


3
回答を投稿していただきありがとうございます。セルフプロモーションに関するFAQをよくお読みください。誰かがこの回答とリンク先のブログ投稿を見て、ここからリンクできるようにブログ投稿を投稿したと思うかもしれません。
Andrew Barber

2
なぜ気になるのstatic {}?その変数の初期化をインライン化して、Pattern final同様に作成できます。
TWiStErRob 2016

1
TWiStErRobの意見の2番目は、private static final Pattern emailPattern = Pattern.compile(EMAIL_PATTERN);より優れています。
Christophe Roussy
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.