Index: C:/Workspace/lucene-trunk/src/java/org/apache/lucene/search/FieldSortedHitQueue.java =================================================================== --- C:/Workspace/lucene-trunk/src/java/org/apache/lucene/search/FieldSortedHitQueue.java (revision 518792) +++ C:/Workspace/lucene-trunk/src/java/org/apache/lucene/search/FieldSortedHitQueue.java (working copy) @@ -178,7 +178,13 @@ comparator = comparatorFloat (reader, fieldname); break; case SortField.STRING: - if (locale != null) comparator = comparatorStringLocale (reader, fieldname, locale); + if (locale != null) { + if (usePerThreadLocaleComparators()) { + comparator = comparatorStringLocalePerThread(reader, fieldname, locale); + } else { + comparator = comparatorStringLocale(reader, fieldname, locale); + } + } else comparator = comparatorString (reader, fieldname); break; case SortField.CUSTOM: @@ -293,45 +299,96 @@ */ static ScoreDocComparator comparatorStringLocale (final IndexReader reader, final String fieldname, final Locale locale) throws IOException { - final Collator collator = Collator.getInstance (locale); final String field = fieldname.intern(); final String[] index = FieldCache.DEFAULT.getStrings (reader, field); + return comparatorStringLocale(reader, index, locale); + } + + /** + * Returns a comparator for sorting hits according to a field containing strings. + * Wraps the comparator in a ThreadLocal so as to avoid lock contention on the + * (fully-synchronized) {@link Collator}. + * @param reader Index to use. + * @param fieldname Fieldable containg string values. + * @return Comparator for sorting hits. + * @throws IOException If an error occurs reading the index. + */ + static ScoreDocComparator comparatorStringLocalePerThread( + final IndexReader reader, final String fieldname, final Locale locale) + throws IOException { + final String field = fieldname.intern(); + final String[] index = FieldCache.DEFAULT.getStrings(reader, field); + return new ScoreDocComparator() { + ThreadLocal perThreadLocal = new ThreadLocal() { + protected Object initialValue() { + return comparatorStringLocale(reader, index, locale); + } + }; - public final int compare(final ScoreDoc i, final ScoreDoc j) { - String is = index[i.doc]; - String js = index[j.doc]; - if (is == js) { - return 0; - } else if (is == null) { - return -1; - } else if (js == null) { - return 1; - } else { - return collator.compare(is, js); - } - } + public int compare(ScoreDoc i, ScoreDoc j) { + return ((ScoreDocComparator) perThreadLocal.get()).compare(i, j); + } - public Comparable sortValue (final ScoreDoc i) { - return index[i.doc]; + public Comparable sortValue(ScoreDoc i) { + return ((ScoreDocComparator) perThreadLocal.get()).sortValue(i); } public int sortType() { - return SortField.STRING; + return ((ScoreDocComparator) perThreadLocal.get()).sortType(); } }; - } + } /** - * Returns a comparator for sorting hits according to values in the given field. - * The terms in the field are looked at to determine whether they contain integers, - * floats or strings. Once the type is determined, one of the other static methods - * in this class is called to get the comparator. + * Returns a comparator for sorting hits according to a field containing strings. * @param reader Index to use. - * @param fieldname Fieldable containg values. + * @param index the Strings obtained from the FieldCache * @return Comparator for sorting hits. * @throws IOException If an error occurs reading the index. */ + private static ScoreDocComparator comparatorStringLocale(final IndexReader reader, final String[] index, final Locale locale) { + final Collator collator = Collator.getInstance (locale); + return new ScoreDocComparator() { + + public final int compare(final ScoreDoc i, final ScoreDoc j) { + String is = index[i.doc]; + String js = index[j.doc]; + if (is == js) { + return 0; + } else if (is == null) { + return -1; + } else if (js == null) { + return 1; + } else { + return collator.compare(is, js); + } + } + + public Comparable sortValue(final ScoreDoc i) { + return index[i.doc]; + } + + public int sortType() { + return SortField.STRING; + } + }; + } + + /** + * Returns a comparator for sorting hits according to values in the given + * field. The terms in the field are looked at to determine whether they + * contain integers, floats or strings. Once the type is determined, one of + * the other static methods in this class is called to get the comparator. + * + * @param reader + * Index to use. + * @param fieldname + * Fieldable containg values. + * @return Comparator for sorting hits. + * @throws IOException + * If an error occurs reading the index. + */ static ScoreDocComparator comparatorAuto (final IndexReader reader, final String fieldname) throws IOException { final String field = fieldname.intern(); @@ -348,4 +405,15 @@ throw new RuntimeException ("unknown data type in field '"+field+"'"); } } -} + + /** + * Should we wrap locale-based sorters in a {@link ThreadLocal}, to avoid + * contention on a {@link Collator Collator's} synchronized methods? Uses + * the system property org.apache.lucene.usePerThreadLocaleComparators, + * which must be set to TRUE to enable this behaviour. + * @return true if locale-based String sorts should be wrapped in a ThreadLocal + */ + private static boolean usePerThreadLocaleComparators() { + return Boolean.getBoolean("org.apache.lucene.usePerThreadLocaleComparators"); + } +} \ No newline at end of file