汇集问题:项目借用不止一次



我有一个我经常调用的实用程序方法(=static方法),它使用java.util.regex.Matcher。由于传递的正则表达式经常被重用,所以我尽量不每次都编译它,所以我将它保存在Map中,其中键是正则表达式,值是Matcher对象的List(这样每个线程都可以获得自己的Matcher实例)。

下面的代码片段是如何两次返回相同的Matcher的。。。有时

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有时确实会返回两次。

(没有显示static实用程序方法,main方法是为了向您展示问题而制作的)

当regexp没有匹配器时,您可以创建一个新的匹配器,但也可以立即将其添加到队列中:

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是个好主意。

当您创建一个新的Matcher时,您会在返回它之前将它提供给Queue,这样下一个线程就会立即获得它。

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

此外,您的duplicateHunter哈希集不是线程安全的,在对其进行验证时可能会给您错误的结果。

最新更新