Java 同步访问 SimpleDateFormat
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4107839/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Synchronizing access to SimpleDateFormat
提问by 3urdoch
The javadoc for SimpleDateFormat states that SimpleDateFormat is not synchronized.
SimpleDateFormat 的 javadoc 声明 SimpleDateFormat 不同步。
"Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally."
“日期格式不同步。建议为每个线程创建单独的格式实例。如果多个线程同时访问一种格式,则必须在外部进行同步。”
But what is the best approach to using an instance of SimpleDateFormat in a multi threaded environment. Here are a few options I have thought of, I have used options 1 and 2 in the past but I am curious to know if there are any better alternatives or which of these options would offer the best performance and concurrency.
但是在多线程环境中使用 SimpleDateFormat 实例的最佳方法是什么?以下是我想到的几个选项,我过去曾使用过选项 1 和 2,但我很想知道是否有更好的替代方案,或者这些选项中的哪一个可以提供最佳的性能和并发性。
Option 1: Create local instances when required
选项 1:根据需要创建本地实例
public String formatDate(Date d) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(d);
}
Option 2: Create an instance of SimpleDateFormat as a class variable but synchronize access to it.
选项 2:创建 SimpleDateFormat 的实例作为类变量,但同步对其的访问。
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
public String formatDate(Date d) {
synchronized(sdf) {
return sdf.format(d);
}
}
Option 3: Create a ThreadLocal to store a different instance of SimpleDateFormat for each thread.
选项 3:创建一个 ThreadLocal 来为每个线程存储一个不同的 SimpleDateFormat 实例。
private ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>();
public String formatDate(Date d) {
SimpleDateFormat sdf = tl.get();
if(sdf == null) {
sdf = new SimpleDateFormat("yyyy-MM-hh");
tl.set(sdf);
}
return sdf.format(d);
}
采纳答案by Peter Knego
Creating SimpleDateFormat is expensive. Don't use this unless it's done seldom.
OK if you can live with a bit of blocking. Use if formatDate() is not used much.
Fastest option IF you reuse threads (thread pool). Uses more memory than 2. and has higher startup overhead.
创建 SimpleDateFormat 是昂贵的。除非很少使用,否则不要使用它。
好吧,如果你能忍受一点阻塞。如果 formatDate() 使用不多,请使用。
如果您重用线程(线程池),则是最快的选择。使用比 2. 更多的内存,并且具有更高的启动开销。
For applications both 2. and 3. are viable options. Which is best for your case depends on your use case. Beware of premature optimization. Only do it if you believe this is an issue.
对于应用程序 2. 和 3. 都是可行的选择。哪种最适合您的情况取决于您的用例。提防过早优化。仅当您认为这是一个问题时才这样做。
For libraries that would be used by 3rd party I'd use option 3.
对于第 3 方将使用的库,我将使用选项 3。
回答by Jed Wesley-Smith
Don't use SimpleDateFormat, use joda-time's DateTimeFormatter instead. It is a bit stricter in the parsing side and so isn't quite a drop in replacement for SimpleDateFormat, but joda-time is much more concurrent friendly in terms of safety and performance.
不要使用 SimpleDateFormat,而是使用 joda-time 的 DateTimeFormatter。它在解析方面更严格,因此并不能完全替代 SimpleDateFormat,但 joda-time 在安全性和性能方面更加并发友好。
回答by Adam Gent
The other option is Commons Lang FastDateFormatbut you can only use it for date formatting and not parsing.
另一个选项是 Commons Lang FastDateFormat但您只能将它用于日期格式而不是解析。
Unlike Joda, it can function as a drop-in replacement for formatting. (Update: Since v3.3.2, FastDateFormat can produce a FastDateParser, which is a drop-in thread-safe replacement for SimpleDateFormat)
与 Joda 不同,它可以作为格式化的替代品。(更新:从 v3.3.2 开始, FastDateFormat 可以生成FastDateParser,这是 SimpleDateFormat的嵌入式线程安全替代品)
回答by Andy
I would say, create a simple wrapper-class for SimpleDateFormat that synchronizes access to parse() and format() and can be used as a drop-in replacement. More foolproof than your option #2, less cumbersome than your option #3.
我会说,为 SimpleDateFormat 创建一个简单的包装类,它同步对 parse() 和 format() 的访问,并且可以用作替代品。比您的选项#2 更万无一失,比您的选项#3 更简单。
Seems like making SimpleDateFormat unsynchronized was a poor design decision on the part of the Java API designers; I doubt anyone expects format() and parse() to need to be synchronized.
似乎让 SimpleDateFormat 不同步对于 Java API 设计者来说是一个糟糕的设计决定;我怀疑有人希望 format() 和 parse() 需要同步。
回答by Vortex
Imagine your application has one thread. Why would you synchronize access to SimpleDataFormat variable then?
想象一下您的应用程序有一个线程。那么为什么要同步对 SimpleDataFormat 变量的访问呢?
回答by Chas
Commons Lang 3.x now has FastDateParser as well as FastDateFormat. It is thread safe and faster than SimpleDateFormat. It also uses the same format/parse pattern specifications as SimpleDateFormat.
Commons Lang 3.x 现在有 FastDateParser 和 FastDateFormat。它是线程安全的并且比 SimpleDateFormat 更快。它还使用与 SimpleDateFormat 相同的格式/解析模式规范。
回答by SamJ
Another option is to keep instances in a thread-safe queue:
另一种选择是将实例保留在线程安全队列中:
import java.util.concurrent.ArrayBlockingQueue;
private static final int DATE_FORMAT_QUEUE_LEN = 4;
private static final String DATE_PATTERN = "yyyy-MM-dd HH:mm:ss";
private ArrayBlockingQueue<SimpleDateFormat> dateFormatQueue = new ArrayBlockingQueue<SimpleDateFormat>(DATE_FORMAT_QUEUE_LEN);
// thread-safe date time formatting
public String format(Date date) {
SimpleDateFormat fmt = dateFormatQueue.poll();
if (fmt == null) {
fmt = new SimpleDateFormat(DATE_PATTERN);
}
String text = fmt.format(date);
dateFormatQueue.offer(fmt);
return text;
}
public Date parse(String text) throws ParseException {
SimpleDateFormat fmt = dateFormatQueue.poll();
if (fmt == null) {
fmt = new SimpleDateFormat(DATE_PATTERN);
}
Date date = null;
try {
date = fmt.parse(text);
} finally {
dateFormatQueue.offer(fmt);
}
return date;
}
The size of dateFormatQueue should be something close to the estimated number of threads which can routinely call this function at the same time. In the worst case where more threads than this number do actually use all the instances concurrently, some SimpleDateFormat instances will be created which cannot be returned to dateFormatQueue because it is full. This will not generate an error, it will just incur the penalty of creating some SimpleDateFormat which are used only once.
dateFormatQueue 的大小应该接近于可以同时调用此函数的估计线程数。在最坏的情况下,比这个数量更多的线程实际上同时使用所有实例,将创建一些 SimpleDateFormat 实例,这些实例无法返回到 dateFormatQueue 因为它已满。这不会产生错误,它只会招致创建一些只使用一次的 SimpleDateFormat 的惩罚。
回答by Paul Vargas
If you are using Java 8, you may want to use java.time.format.DateTimeFormatter
:
如果您使用的是 Java 8,则可能需要使用java.time.format.DateTimeFormatter
:
This class is immutable and thread-safe.
这个类是不可变的和线程安全的。
e.g.:
例如:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String str = new java.util.Date().toInstant()
.atZone(ZoneId.systemDefault())
.format(formatter);
回答by mwk
I just implemented this with Option 3, but made a few code changes:
我刚刚使用选项 3 实现了这一点,但做了一些代码更改:
- ThreadLocal should usually be static
- Seems cleaner to override initialValue() rather than test if (get() == null)
You may want to set locale and time zone unless you really want the default settings (defaults are very error prone with Java)
private static final ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>() { @Override protected SimpleDateFormat initialValue() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-hh", Locale.US); sdf.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); return sdf; } }; public String formatDate(Date d) { return tl.get().format(d); }
- ThreadLocal 通常应该是静态的
- 覆盖 initialValue() 而不是测试 if (get() == null) 似乎更干净
您可能想要设置语言环境和时区,除非您真的想要默认设置(Java 的默认设置很容易出错)
private static final ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>() { @Override protected SimpleDateFormat initialValue() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-hh", Locale.US); sdf.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); return sdf; } }; public String formatDate(Date d) { return tl.get().format(d); }