在抽象的JDK类Formatter中,用于格式化调试日志,formatMessage方法被声明为synchronized。
但是,我无法确定原因。
显然,可以编写非线程安全的覆盖版本,但我想知道为什么默认实现不是线程安全的。
答案 0 :(得分:4)
[T]他将formatMessage方法声明为synchronized.However,我无法确定为什么会这样。
早期版本的java.util.logging.Formatter尝试缓存资源包获取字符串调用的结果,以避免构造java.util.MissingResourceException
。同步用于保护用于缓存查找的HashMap
。
这是版权所有2004 Sun Microsystems,Inc。版本1.16,12 / 19/03源代码,请注意评论:
public synchronized String formatMessage(LogRecord record) {
String format = record.getMessage();
java.util.ResourceBundle catalog = record.getResourceBundle();
if (catalog != null) {
// We cache catalog lookups. This is mostly to avoid the
// cost of exceptions for keys that are not in the catalog.
// if (catalogCache == null) {
// catalogCache = new HashMap();
// }
// format = (String)catalogCache.get(record.essage);
// if (format == null) {
try {
format = catalog.getString(record.getMessage());
} catch (java.util.MissingResourceException ex) {
// Drop through. Use record message as format
format = record.getMessage();
}
// catalogCache.put(record.message, format);
// }
}
// Do the formatting.
try {
Object parameters[] = record.getParameters();
if (parameters == null || parameters.length == 0) {
// No parameters. Just return format string.
return format;
}
// Is is a java.text style format?
// Ideally we could match with
// Pattern.compile("\\{\\d").matcher(format).find())
// However the cost is 14% higher, so we cheaply check for
// 1 of the first 4 parameters
if (format.indexOf("{0") >= 0 || format.indexOf("{1") >=0 ||
format.indexOf("{2") >=0|| format.indexOf("{3") >=0) {
return java.text.MessageFormat.format(format, parameters);
}
return format;
} catch (Exception ex) {
// Formatting failed: use localized format string.
return format;
}
}
我想知道为什么默认实现不是线程安全的。
当缓存代码被注释掉时,应该已经删除了同步。此问题在JDK-8153666: Possible optimization of Formatter.formatMessage下提交。
它确实在OpenJDK中同步,但可能不在JavaDocs
中
从签名中删除“synchronized”和“native”。 Javadoc生成API规范。这两个关键字不属于规范的签名,因为它们是特定于实现的。关键字“native”不需要记录。关键字“synchronized”表示应该在方法描述中描述的线程安全行为。线程安全方法本身可能不使用“synchronized”关键字,但可能会调用私有方法。
P.S。
record.essage
是原始代码中的实际拼写错误。record.getMessage()
多次调用多次,即使它在第一次调用时存储在本地。catalog.getString
,这将导致NPE失败。