2012-01-19 4 views
1

私はjava.util.regex.Matcherを使用するユーティリティメソッド(= static)をたくさん持っています。渡された正規表現が多く再利用されているので、毎回コンパイルしないようにしています。Mapのキーがそのまま正規表現であり、値はListMatcherオブジェクトです(各スレッドは自分自身にMatcherインスタンス)。プールの問題:複数回借りたアイテム

次のコードスニペットは、同じMatcherを2回返すことがあります。

import java.util.HashMap; 
import java.util.HashSet; 
import java.util.Map; 
import java.util.Queue; 
import java.util.Set; 
import java.util.concurrent.ConcurrentLinkedQueue; 
import java.util.regex.Matcher; 
import java.util.regex.Pattern; 

public class MyTest { 

    private static final Map<String, Queue<Matcher>> matchers = new HashMap<String, Queue<Matcher>>(); 
    private static Set<Integer> duplicateHunter = new HashSet<Integer>(); 

    private static Matcher getMatcher(String regexp, String value) { 
     Queue<Matcher> matcherQueue = matchers.get(regexp); 

     if (matcherQueue == null) { 
      synchronized (matchers) { 
       matcherQueue = matchers.get(regexp); 

       if (matcherQueue == null) { 
        // Create a new thread-safe Queue and a new Matcher 
        matcherQueue = new ConcurrentLinkedQueue<Matcher>(); 
        matchers.put(regexp, matcherQueue); 
       } // Else: another thread already did what needed to be done 
      } 
     } 

     // Try to retrieve a Matcher 
     Matcher matcher = matcherQueue.poll(); 

     if (matcher == null) { 
      // No matchers available, create one 
      // No lock needed, as it's not a drama to have a few more matchers in the queue 
      Pattern pattern = Pattern.compile(regexp); 
      matcher = pattern.matcher(value); 
      matcherQueue.offer(matcher); 
     } else { 
      // reset the matcher 
      matcher.reset(value); 
     } 

//  boolean notADuplicate = duplicateHunter.add(matcher.hashCode()); 
//  if(!notADuplicate) { 
//   throw new RuntimeException("DUPLICATE!!!"); 
//  } 

     return matcher; 
    } 

    private static void returnMatcher(String regex, Matcher matcher) { 
     Queue<Matcher> matcherQueue = matchers.get(regex); 
     //duplicateHunter.remove(matcher.hashCode()); 
     matcherQueue.offer(matcher); 
    } 

    public static void main(String[] args) { 

     for (int i = 0; i < 2; i++) { 
      Thread thread = new Thread(new Runnable() { 

       public void run() { 
        for (int i = 0; i < 50000; i++) { 
         String regex = ".*"; 
         Matcher matcher = null; 
         try { 
          matcher = getMatcher(regex, "toto" + i); 
          if (matcher.matches()) { 
           // matches 
          } 
         } finally { 
          if (matcher != null) { 
           returnMatcher(regex, matcher); 
          } 
         } 
        } 


       } 
      }); 

      thread.start(); 
     } 

    } 
} 

java.lang.StringIndexOutOfBoundsException:文字列インデックスが範囲外です。」が表示されます。 duplicateHunterコードを有効にすると、実際にMatcherが2回返されることがあります。

staticユーティリティメソッドが示されていない、main方法があなたの問題を表示するためになされた)

+2

なぜパターンの代わりにマッチャーをキャッシュしていますか?パターンが不変のスレッドセーフであるため、後者のアプローチがはるかに簡単になると思いました。 –

+0

なぜ単にcommons-poolを使用しないのですか? – artbristol

+0

@ジョン:別の質問の回答(http://stackoverflow.com/questions/7505160/high-performance-simple-java-regular-expressions)では、マッチャーを再利用するとパフォーマンスが「いくらか」向上すると言われています。そこにリンクされたマイクロベンチ(http://pastie.org/2570213)は、この小さなパフォーマンス向上を示しました。私はマイクロベンチがJavaで悪いことを知っていますが、私はまだ何とか確信していました。 – AndrewBourgeois

答えて

4

正規表現のためのマッチャがない場合、あなたは新しいマッチャーを作成しますが、それを追加すぐにキュー:

if (matcher == null) { 
    // No matchers available, create one 
    // No lock needed, as it's not a drama to have a few more matchers in the queue 
    Pattern pattern = Pattern.compile(regexp); 
    matcher = pattern.matcher(value); 
    matcherQueue.offer(matcher); // Don't add it to the queue here! 
} 

あなたがそれを使用している間、このように、それはキューになり、作業が完了する前に、別のスレッドが簡単にそれを手に入れることができます。

途中でマッチャーをプールするというあなたの考えに同意するかどうか分かりません。 CPUサイクルの観点からは、それほど高価ではありません。あなたはおそらくそれが価値があるかどうかを見るためにそれをプロファイルしたいと思うでしょう。ただし、Patternをプリコンパイルするのは良い考えです。

+0

これは確かに非常に賢いことではありませんありがとう、ありがとう! Jon SkeetはMatcherについて同じ考えを持っていました。私は数GBのテキストと実際の正規表現でテストしなければならないと思います。 – AndrewBourgeois

+0

うん、それを試して!これは、getMatcher()が新しいものをインスタンス化するよりも速いかどうかを知る唯一の方法です。ああ、長期実行ジョブの場合は、Javaを '-server'フラグで使用していることを確認したいので、インライン化についてもっと積極的です。これにより、新しいマッチャーに割り当てられるメモリの量を減らすことさえできます。 – waxwing

1

新しいMatcherを作成すると、それを返す前にQueueに提供し、次のスレッドがすぐにそれを取得します。

matcher = pattern.matcher(value); 
matcherQueue.offer(matcher);  // <-- this line should be taken taken out and shot 

... 

return matcher; 

また、duplicateHunter HashSetはスレッドセーフではなく、検証時に間違った結果をもたらすことがあります。

関連する問題