使用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™ 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 "Details" 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%;在我运行测试的任何(类)机器上,该增益都是恒定的
此解决方案的一个明显缺点是,使用myformat()
实现的每个线程的内存占用增加了大约半kB加上初始缓冲区的大小(请参阅源代码)
但我隐约记得一些说法,即使用java.lang.ThreadLocal
是邪恶的,尽管我不记得任何原因……基本上,你可以把它归结为“有谣言……”
还是不?在使用java.util.ThreadLocal
的过程中,我是否遗漏了一些东西
请不要讨论用我的解决方案替换String.format()
是否有用:正如我所说的那样,我只是这么做了,因为我想知道是否可以通过缓存Formatter
实例来加快速度,除此之外没有别的
编辑(基于添加到问题中的一些评论):
- 那