使用java.lang.ThreadLocal是否有任何一般缺点?

使用java.lang.ThreadLocal是否有任何一般缺点?,java,multithreading,thread-local,thread-local-storage,Java,Multithreading,Thread Local,Thread Local Storage,每次使用String.format(),都会创建一个java.util.Formatter的新实例,该实例将在以后处理 正因为我很好奇,我对它进行了一些研究,通过缓存Formatter对象,使format()的使用效率更高一些。不幸的是,格式化程序不是线程安全的,任何同步方法都会消耗您从缓存中获得的任何性能增益 最后,我找到了以下解决方案: import java.util.Formatter; /*------------------------*\ ====** Static In

每次使用
String.format()
,都会创建一个
java.util.Formatter
的新实例,该实例将在以后处理

正因为我很好奇,我对它进行了一些研究,通过缓存
Formatter
对象,使
format()
的使用效率更高一些。不幸的是,
格式化程序
不是线程安全的,任何同步方法都会消耗您从缓存中获得的任何性能增益

最后,我找到了以下解决方案:

import java.util.Formatter;

    /*------------------------*\
====** Static Initialisations **===========================================
    \*------------------------*/
/**
 *  The initial buffer size that is used by
 *  {@link #format(String, Object...)}
 *  and
 *  {@link #format(Locale, String, Object...)}
 *  (per thread): {@value}.
 */
public final static int FORMAT_INITIAL_BUFFERSIZE = 2048;

/**
 *  The cached
 *  {@link Formatter}
 *  instance.
 */
private static final ThreadLocal<Formatter> m_Formatter;

static
{
    //---* The cached Formatter instance *---------------------------------
    final Supplier<Formatter> initializer = () -> new Formatter( new StringBuilder( FORMAT_INITIAL_BUFFERSIZE ), Locale.getDefault( Locale.Category.FORMAT ) );
    m_Formatter = ThreadLocal.<Formatter>withInitial( initializer );
}

    /*---------*\
====** Methods **==========================================================
    \*---------*/
/**
 *  Returns a formatted String using the specified
 *  {@link Locale},
 *  format String, and arguments.<br>
 *  <br>This method is meant as a replacement for the method
 *  {@link java.lang.String#format(String, Object...)};
 *  that implementation always uses a new instance of
 *  {@link Formatter}
 *  for each invocation. The implementation here uses a cached instance
 *  instead, and as {@code Formatter} is not inherently thread-safe, this
 *  cached instance is created and stored per thread, therefore no
 *  synchronisation is needed.<br>
 *  <br>The performance gain for this implementation is not that impressive
 *  when compared with that of
 *  {@link #format(String, Object...)},
 *  but still in the 10% range.
 *  {@code java.lang.String.format()}.
 *
 *  @param  locale  The
 *      {@link Locale}
 *      that will be applied during formatting. If {@code locale} is
 *      {@code null} then no localisation is applied.
 *  @param  format  A format String with the syntax as described for the
 *      {@link Formatter}
 *      class.
 *  @param  args    The arguments referenced by the format specifiers in
 *      the {@code format} String. If there are more arguments than format
 *      specifiers, the extra arguments are ignored. The number of
 *      arguments is variable and may be zero. The maximum number of
 *      arguments is limited by the maximum dimension of a Java array as
 *      defined by <cite>The Java&trade; Virtual Machine
 *      Specification</cite>. The behaviour on a {@code null} argument
 *      depends on the conversion.
 *  @throws IllegalFormatException A format string contains an illegal
 *      syntax, a format specifier that is incompatible with the given
 *      arguments, insufficient arguments given the format string, or other
 *      illegal conditions occurred. For specification of all possible
 *      formatting errors, see the &quot;Details&quot; section of the
 *      {@code Formatter} class specification.
 *  @return A formatted String.
 *
 *  @see  java.util.Formatter
 */
@SuppressWarnings( "resource" )
public final static String format( final Locale locale, final String format, final Object... args ) throws IllegalFormatException
{
    /*
     * When this method StringUtils.format() is used inside an
     * implementation of Formattable.formatTo() that in turn was called by
     * this method already, the internal out buffer of the formatter
     * contains already some text that has to be preserved.
     *
     * Therefore the current length of the out buffer is kept before new
     * contents is added by the call to formatter.format(), and reset after
     * the new formatted text was retrieved from that buffer.
     */
    final var formatter = m_Formatter.get();
    final var result = (StringBuilder) formatter.out();
    final var currentPos = result.length();
    formatter.format( locale, format, args );
    final var retValue = result.substring( currentPos );
    result.setLength( currentPos );

    //---* Done *----------------------------------------------------------
    return retValue;
}   //  format()
import java.util.Formatter;
/*------------------------*\
=**静态初始化**===========================================
\*------------------------*/
/**
*所使用的初始缓冲区大小
*{@link#格式(字符串、对象…)
*及
*{@link#格式(区域设置、字符串、对象…)
*(每个线程):{@value}。
*/
公共最终静态整数格式\初始\缓冲区大小=2048;
/**
*缓存的
*{@link格式化程序}
*例如。
*/
专用静态最终线程本地m_格式化程序;
静止的
{
//---*缓存的格式化程序实例*---------------------------------
最终供应商初始值设定项=()->新的格式化程序(新的StringBuilder(FORMAT_INITIAL_BUFFERSIZE)、Locale.getDefault(Locale.Category.FORMAT));
m_Formatter=ThreadLocal.withInitial(初始值设定项);
}
/*---------*\
==**方法**==========================================================
\*---------*/
/**
*使用指定的
*{@link Locale},
*格式化字符串和参数。
*
此方法旨在替代该方法 *{@link java.lang.String#格式(字符串,对象…); *该实现始终使用 *{@link格式化程序} *对于每次调用。这里的实现使用缓存实例 *相反,由于{@code Formatter}本身不是线程安全的,因此 *缓存实例是按线程创建和存储的,因此 *需要同步。
*
此实现的性能增益并不令人印象深刻 *与美国相比, *{@link#格式(字符串、对象…), *但仍在10%的范围内。 *{@code java.lang.String.format()}。 * *@param语言环境 *{@link Locale} *这将在格式化期间应用。如果{@code locale}为 *{@code null}则不应用本地化。 *@param format格式字符串,其语法与 *{@link格式化程序} *班级。 *@param参数由中的格式说明符引用的参数 *{@code format}字符串。如果参数多于格式 *说明符,则忽略额外的参数。人数 *参数是可变的,可以是零。最大数量的 *参数受Java数组的最大维数限制,如下所示: *由Java&trade定义;虚拟机 *规格。{@code null}参数的行为 *取决于转换。 *@throws-IllegalFormatException格式字符串包含非法 *语法,一个与给定语法不兼容的格式说明符 *参数,给定格式字符串的参数不足,或其他 *出现了非法情况。对于所有可能的 *格式错误,请参阅 *{@code Formatter}类规范。 *@返回格式化的字符串。 * *@see java.util.Formatter */ @抑制警告(“资源”) 公共最终静态字符串格式(最终区域设置、最终字符串格式、最终对象…args)引发IllegalFormatException { /* *在内部使用此方法StringUtils.format()时 *Formattable.formatTo()的实现,而Formattable.formatTo()又被 *此方法已存在,格式化程序的内部输出缓冲区 *已包含一些必须保留的文本。 * *因此,输出缓冲区的当前长度保持在新缓冲区之前 *通过调用formatter.format()添加内容,并在 *已从该缓冲区检索到新的格式化文本。 */ final var formatter=m_formatter.get(); 最终变量结果=(StringBuilder)formatter.out(); 最终变量currentPos=结果.长度(); formatter.format(区域设置、格式、参数); 最终var retValue=结果子串(currentPos); 结果:设置长度(currentPos); //---*完成*---------------------------------------------------------- 返回值; }//格式()
缓存的
格式化程序
实例保存在
java.lang.ThreadLocal
的实例中,因此每个线程都有自己的实例,不需要同步

到目前为止,它正在发挥作用;到目前为止,广泛的测试没有发现任何问题,一些(人工)基准测试显示,与使用
java.lang.String.format()
相比,性能提高了约10%;在我运行测试的任何(类)机器上,该增益都是恒定的

此解决方案的一个明显缺点是,使用my
format()
实现的每个线程的内存占用增加了大约半kB加上初始缓冲区的大小(请参阅源代码)

但我隐约记得一些说法,即使用
java.lang.ThreadLocal
是邪恶的,尽管我不记得任何原因……基本上,你可以把它归结为“有谣言……”

还是不?在使用
java.util.ThreadLocal
的过程中,我是否遗漏了一些东西

请不要讨论用我的解决方案替换
String.format()
是否有用:正如我所说的那样,我只是这么做了,因为我想知道是否可以通过缓存
Formatter
实例来加快速度,除此之外没有别的

编辑(基于添加到问题中的一些评论):