同步对 SimpleDateFormat 的访问

发布于 2024-09-30 11:39:35 字数 1070 浏览 8 评论 0原文

SimpleDateFormat 的 javadoc 声明 SimpleDateFormat 不同步。

“日期格式不同步。它 建议单独创建 为每个线程格式化实例。如果 多个线程访问一种格式 同时,必须同步 外部。”

在多线程环境中使用 SimpleDateFormat 实例的最佳方法是什么。以下是我想到的一些选项,我过去使用过选项 1 和 2,但我很想知道是否有是否有更好的替代方案,或者哪个选项可以提供最佳性能和并发性

选项 1:在需要时创建本地实例 选项

public String formatDate(Date d) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    return sdf.format(d);
}

2:创建 SimpleDateFormat 的实例作为类变量,但同步对其的访问

private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
public String formatDate(Date d) {
    synchronized(sdf) {
        return sdf.format(d);
    }
}

选项 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);
}

The javadoc for SimpleDateFormat states that SimpleDateFormat is not synchronized.

"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.

Option 1: Create local instances when required

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.

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.

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);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

可是我不能没有你 2024-10-07 11:39:35
  1. 创建 SimpleDateFormat 是

  2. 如果你能忍受一点阻塞,那就好。如果 formatDate() 不常用,则使用。

  3. 如果您重用线程,则最快的选项(线程池) 。使用比 2 更多的内存,并且启动开销更高。

对于应用程序,2. 和 3. 都是可行的选择。哪种最适合您的情况取决于您的用例。谨防过早优化。仅当您认为这是一个问题时才这样做。

对于第三方使用的库,我会使用选项 3。

  1. Creating SimpleDateFormat is expensive. Don't use this unless it's done seldom.

  2. OK if you can live with a bit of blocking. Use if formatDate() is not used much.

  3. Fastest option IF you reuse threads (thread pool). Uses more memory than 2. and has higher startup overhead.

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.

For libraries that would be used by 3rd party I'd use option 3.

晒暮凉 2024-10-07 11:39:35

另一个选项是 Commons Lang FastDateFormat 但你只能用它来格式化日期,而不能用来解析。

与 Joda 不同,它可以作为格式化的直接替代品。
(更新:从 v3.3.2 开始,FastDateFormat 可以生成 FastDateParser,它是 SimpleDateFormat 的直接线程安全替代品)

The other option is Commons Lang FastDateFormat but you can only use it for date formatting and not parsing.

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)

看海 2024-10-07 11:39:35

如果您使用的是 Java 8,则可能需要使用 <代码>java.time.format.DateTimeFormatter

此类是不可变的且线程安全的。

例如:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String str = new java.util.Date().toInstant()
                                 .atZone(ZoneId.systemDefault())
                                 .format(formatter);

If you are using Java 8, you may want to use 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);
寄与心 2024-10-07 11:39:35

Commons Lang 3.x 现在拥有 FastDateParser 和 FastDateFormat。它是线程安全的并且比 SimpleDateFormat 更快。它还使用与 SimpleDateFormat 相同的格式/解析模式规范。

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.

是你 2024-10-07 11:39:35

不要使用 SimpleDateFormat,而是使用 joda-time 的 DateTimeFormatter。它在解析方面有点严格,因此并不能完全替代 SimpleDateFormat,但 joda-time 在安全性和性能方面对并发更加友好。

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.

初见 2024-10-07 11:39:35

我想说,为 SimpleDateFormat 创建一个简单的包装类,它同步对 parse() 和 format() 的访问,并且可以用作直接替换。比你的选项 #2 更简单,比你的选项 #3 更简单。

对于 Java API 设计者来说,使 SimpleDateFormat 不同步似乎是一个糟糕的设计决策;我怀疑有人期望 format() 和 parse() 需要同步。

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.

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.

全部不再 2024-10-07 11:39:35

另一种选择是将实例保留在线程安全队列中:

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;
}

dateFormatQueue 的大小应该接近可以同时例行调用此函数的估计线程数。
在最坏的情况下,实际并发使用所有实例的线程数超过此数量,将创建一些 SimpleDateFormat 实例,这些实例无法返回到 dateFormatQueue,因为它已满。这不会产生错误,只会招致创建一些仅使用一次的 SimpleDateFormat 的惩罚。

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.

等风来 2024-10-07 11:39:35

我刚刚使用选项 3 实现了这一点,但做了一些代码更改:

  • ThreadLocal 通常应该是静态的
  • 看起来更干净,可以覆盖 initialValue() 而不是测试 if (get() == null)
  • 您可能想要设置区域设置和时间除非您确实想要默认设置(Java 中的默认设置很容易出错)

    private static final ThreadLocal; tl = new ThreadLocal() {
        @覆盖
        受保护的 SimpleDateFormat 初始值(){
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-hh", Locale.US);
            sdf.setTimeZone(TimeZone.getTimeZone("美国/洛杉矶"));
            返回 sdf;
        }
    };
    公共字符串格式日期(日期d){
        return tl.get().format(d);
    }
    

I just implemented this with Option 3, but made a few code changes:

  • 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);
    }
    
葬心 2024-10-07 11:39:35

想象一下您的应用程序有一个线程。那么为什么要同步对 SimpleDataFormat 变量的访问呢?

Imagine your application has one thread. Why would you synchronize access to SimpleDataFormat variable then?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文