不建议调用静态java.text.DateFormat方法?
我收到一个查找bug错误-调用静态java.text.DateFormat方法 我不知道为什么在下面做这些事情不好/不可取不建议调用静态java.text.DateFormat方法?,java,static,Java,Static,我收到一个查找bug错误-调用静态java.text.DateFormat方法 我不知道为什么在下面做这些事情不好/不可取 private static final Date TODAY = Calendar.getInstance().getTime(); private static final DateFormat yymmdd = new SimpleDateFormat("yyMMdd"); private String fileName = "file_" + yymmdd.fo
private static final Date TODAY = Calendar.getInstance().getTime();
private static final DateFormat yymmdd = new SimpleDateFormat("yyMMdd");
private String fileName = "file_" + yymmdd.format(TODAY);
首先,它不是线程安全的。日期格式不是线程安全的,这意味着它们维护状态的内部表示。如果多个线程同时访问同一个实例,在静态上下文中使用它们可能会产生一些非常奇怪的错误 我的建议是将变量设置为使用它们的地方的局部变量,而不是将它们设置为类的静态属性。在初始化类时,您可能正在执行此操作,因此您可以在构造函数中执行此操作:
public class MyClass {
private String fileName;
public MyClass() {
final Date today = Calendar.getInstance().getTime();
final DateFormat yymmdd = new SimpleDateFormat("yyMMdd");
this.fileName = "file_" + yymmdd.format(TODAY);
}
...
}
如果您需要在多个位置使用格式化程序,您可以在需要时将模式设置为静态最终模式,并创建一个新的本地DateFormat
:
public class MyClass {
private static final String FILENAME_DATE_PATTERN = "yyMMdd";
public void myMethod() {
final DateFormat format = new SimpleDateFormat(FILENAME_DATE_PATTERN);
// do some formatting
}
}
该问题的答案是:
正如JavaDoc所述,日期格式是
对于多线程系统来说本质上是不安全的
使用。探测器找到了一个呼叫
DateFormat的一个实例,具有
通过一个静态场获得。这
看起来可疑
有关此功能的更多信息,请参见Sun
臭虫6231579和太阳虫6178997
报告建议:
日期格式不同步。信息技术
建议创建单独的
格式化每个线程的实例。如果
多线程访问一种格式
同时,它必须同步
外部
关于静态使用“TODAY”的语义也有一个很好的观点
顺便说一句,我实际上在高流量的生产环境中看到过这种情况,开始调试是一件非常令人困惑的事情;因此,根据我的经验,FindBugs警告实际上是一个有用的建议(与其他一些静态分析规则不同,这些规则有时似乎很挑剔)。你确定不是吗
private static final DateFormat yymmdd = new SimpleDateFormat("yyMMdd");
??这就是错误消息所指示的
我认为它的目标是
DateFormat
不是线程安全的,因此将实例作为静态字段表示潜在的竞争条件。我认为这是因为格式不是线程安全的
(我没有看到findbugs抱怨什么,你能提供
警告文本?您可以通过在同步块中包装对DateFormat的所有引用来消除此问题-只需确保所有调用都包装在同一个同步对象中 我不确定FindBugs是否在抱怨这一点,但我看到您的代码存在一个问题,即您现在将
定义为类级(静态)、常量(最终)变量。这传达了您希望今天
永不改变的意图(我不认为是这样,因为java.util.Dates是可变的,但这是另一回事)
考虑一下如果应用程序运行多天会发生什么今天(除非更新)将引用应用程序启动的日期,而不是当前日期。你确定这就是你的意思吗
这可能根本不是代码中的一个bug,但目的并不明确,我相信这可能就是FindBugs所抱怨的。有一个线程安全的对象。
但它只进行格式化,不进行解析
如果你能使用commons-lang,这可能会对你很有用
private static final Date TODAY = Calendar.getInstance().getTime();
private static final FastDateFormat yymmdd = FastDateFormat.getInstance("yyMMdd");
private String fileName = "file_" + yymmdd.format(TODAY);
另一种未提及的方法是使用ThreadLocal。有关3个选项之间的更多信息和性能比较,请参阅:
- 每次创建一个实例
- 同步存取
- 使用ThreadLocal
使用ThreadLocal的示例:
private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyMMdd");
}
};
撇开调用方法不谈,这段代码看起来确实有点可疑-今天将是一个固定的日子,格式化程序是最终的非静态的?而且今天可能并不总是“今天”,例如,如果加载这个类,然后让JVM运行到第二天-任何依赖今天作为当前日期的逻辑都不会工作,除非您要解释这种差异。@Peter-是的,每次运行都会重置程序。这可能会消除FindBugs错误,但在需要时只更新一个本地日期格式不是更符合逻辑吗?@Rob,我想这取决于同步是否真的可能会偶尔阻塞一次,或者只是为了安抚FindBugs的礼节。如果创建一个新的DateFormat的开销在某种程度上影响了系统性能,我想同步可能是一种替代方法,但我怀疑在大多数情况下它是否会影响系统性能。另外,如果是这样的话,它可能是一个负载更高的系统,我想同步也会以类似的方式阻碍性能。我不太确定,我认为每次调用myMethod()时创建SimpleDataFormat太贵了。@eradicus-你有性能指标证明它“太贵”吗?除非您已经注意到它是应用程序的瓶颈,否则我宁愿使用局部方法,而不是使用static final
s来扰乱全局范围。强制性克努斯:为什么变量是“最终的”,即常量还不够?如果它不能更改,所有线程都应该能够共享它,不是吗?或者这是初始化的竞争条件问题?final
只意味着不能重新分配引用-引用点所在对象的内容不能从存在final
中得到任何保证。
DATE_FORMAT.get().format( TODAY )