it-swarm.com.de

Zugriff auf SimpleDateFormat synchronisieren

Der Javadoc für SimpleDateFormat gibt an, dass SimpleDateFormat nicht synchronisiert ist.

Msgstr "Datumsformate werden nicht synchronisiert. Es wird empfohlen, für jeden Thread separate Formatinstanzen zu erstellen. Wenn mehrere Threads gleichzeitig auf ein Format Zugreifen, muss es extern synchronisiert werden."

Was ist der beste Ansatz, um eine Instanz von SimpleDateFormat in einer Umgebung mit mehreren Threads zu verwenden? Hier sind ein paar Optionen, an die ich gedacht habe, ich habe in der Vergangenheit die Optionen 1 und 2 verwendet, aber ich bin neugierig, ob es bessere Alternativen gibt oder welche dieser Optionen die beste Leistung und Parallelität bietet.

Option 1: Erstellen Sie bei Bedarf lokale Instanzen

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

Option 2: Erstellen Sie eine Instanz von SimpleDateFormat als Klassenvariable, synchronisieren Sie jedoch den Zugriff darauf.

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

Option 3: Erstellen Sie ein ThreadLocal, um für jeden Thread eine andere Instanz von SimpleDateFormat zu speichern.

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);
}
84
3urdoch
  1. Das Erstellen von SimpleDateFormat ist teuer . Verwenden Sie das nicht, es sei denn, es wird selten gemacht.

  2. OK, wenn Sie mit etwas Blockieren leben können. Verwenden Sie dies, wenn formatDate () nicht häufig verwendet wird.

  3. Schnellste Option, WENN Sie Threads wiederverwenden ( Threadpool ). Verwendet mehr Arbeitsspeicher als 2. und hat einen höheren Systemstartaufwand.

Für Anwendungen sind sowohl 2. als auch 3. realisierbare Optionen. Welches für Ihren Fall am besten ist, hängt von Ihrem Anwendungsfall ab. Vorsicht vor vorzeitiger Optimierung. Tun Sie es nur, wenn Sie glauben, dass dies ein Problem ist.

Für Bibliotheken, die von Drittanbietern verwendet werden, würde ich Option 3 verwenden.

42
Peter Knego

Die andere Option ist Commons Lang FastDateFormat. Sie können sie jedoch nur zur Datumsformatierung und nicht zum Analysieren verwenden. 

Im Gegensatz zu Joda kann es als Drop-In-Ersatz für die Formatierung fungieren . (Update: Seit v3.3.2 kann FastDateFormat einen FastDateParser erzeugen, der ein Drop-in-Thread-sicherer Ersatz für SimpleDateFormat ist)

24
Adam Gent

Wenn Sie Java 8 verwenden, möchten Sie möglicherweise Java.time.format.DateTimeFormatter verwenden:

Diese Klasse ist unveränderlich und threadsicher.

z.B.:

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

Commons Lang 3.x hat jetzt FastDateParser sowie FastDateFormat. Es ist Thread-sicher und schneller als SimpleDateFormat. Es verwendet auch dieselben Format-/Analyse-Musterspezifikationen wie SimpleDateFormat. 

6
Chas

Verwenden Sie SimpleDateFormat nicht, sondern DateTimeFormatter von joda-time. Im Parsing-Bereich ist es etwas strenger, was SimpleDateFormat nicht ersetzen kann, aber joda-time ist in Bezug auf Sicherheit und Leistung wesentlich freundlicher.

4

Ich würde sagen, eine einfache Wrapper-Klasse für SimpleDateFormat erstellen, die den Zugriff auf parse () und format () synchronisiert und als Drop-In-Ersatz verwendet werden kann. Idiotensicherer als Ihre Option # 2, weniger umständlich als Ihre Option # 3.

Es scheint so, als ob SimpleDateFormat nicht synchronisiert wurde, war eine schlechte Entwurfsentscheidung seitens der Java-API-Designer. Ich bezweifle, dass jemand erwartet, dass format () und parse () synchronisiert werden müssen.

3
Andy

Ich habe dies gerade mit Option 3 implementiert, aber ein paar Codeänderungen vorgenommen:

  • ThreadLocal sollte normalerweise statisch sein
  • Es scheint sauberer zu sein, initialValue () zu überschreiben, anstatt zu testen, ob (get () == null)
  • Möglicherweise möchten Sie Gebietsschema und Zeitzone festlegen, es sei denn, Sie möchten wirklich die Standardeinstellungen (Standardeinstellungen sind mit Java sehr fehleranfällig).

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

Eine weitere Option besteht darin, Instanzen in einer Thread-sicheren Warteschlange zu behalten:

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

Die Größe von dateFormatQueue sollte in etwa der geschätzten Anzahl von Threads entsprechen, die diese Funktion routinemäßig gleichzeitig aufrufen können .. Im schlimmsten Fall, wenn mehr Threads als diese Anzahl tatsächlich alle Instanzen gleichzeitig verwenden, werden einige SimpleDateFormat-Instanzen verwendet erstellt werden, das nicht an dateFormatQueue zurückgegeben werden kann, da es voll ist. Dadurch wird kein Fehler generiert, sondern es besteht die Strafe, dass einige SimpleDateFormat erstellt werden, die nur einmal verwendet werden.

1
SamJ

Stellen Sie sich vor, Ihre Anwendung hat einen Thread. Warum sollten Sie dann den Zugriff auf die Variable SimpleDateFormat synchronisieren?

0
Vortex