SimpleDateFormatへのアクセスの同期


90

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:スレッドごとにSimpleDateFormatの異なるインスタンスを格納するThreadLocalを作成します。

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

10
この問題を提起するための+1。SimpleDateFormatはスレッドセーフであると多くの人が考えています(私は至る所で仮定を確認しています)。
アダムゲント

ThreadLocalアプローチの詳細については、次を参照してください:javaspecialists.eu/archive/Issue172.html
miner49r

:そして、なぜのために、この質問を参照stackoverflow.com/questions/6840803/...
レドワルド

@ 3urdochオプション2で「static」キーワードを誤ってスキップしましたか?
Mファイサル

回答:


43
  1. SimpleDateFormatの作成にはコストかかります。めったに行われない限り、これを使用しないでください。

  2. 少しのブロッキングで生きていけるならOK。formatDate()があまり使用されない場合に使用します。

  3. スレッドを再利用する場合の最速のオプション(スレッドプール)。2.よりも多くのメモリを使用し、起動時のオーバーヘッドが高くなります。

アプリケーションでは、2。と3.の両方が実行可能なオプションです。どちらがケースに最適かは、ユースケースによって異なります。時期尚早の最適化に注意してください。これが問題であると思われる場合にのみ行ってください。

サードパーティが使用するライブラリの場合は、オプション3を使用します。


Option-2を使用SimpleDateFormatし、インスタンス変数として宣言すると、synchronized blockスレッドセーフにすることができます。しかし、ソナーは警告イカ-AS2885を示しています。ソナーの問題を解決する方法はありますか?
Mファイサルハメード

24

もう1つのオプションはCommons Lang FastDateFormatですが、これは日付の書式設定にのみ使用でき、解析には使用できません。

Jodaとは異なり、フォーマットのドロップイン置換として機能できます。(アップデート:V3.3.2ので、FastDateFormatを生成することができるFastDateParserドロップインてSimpleDateFormatのためのスレッドセーフな置換です)


8
Commons Lang 3.2以降、メソッドもFastDateFormat持ってparse()います
マナナ2014

19

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

6

Commons Lang 3.xにFastDateParserとFastDateFormatが追加されました。SimpleDateFormatよりもスレッドセーフで高速です。また、SimpleDateFormatと同じフォーマット/解析パターン仕様を使用します。


3.2以降でのみ利用可能で、3.xでは利用できません
Wisteso 2017

4

SimpleDateFormatを使用せず、代わりにjoda-timeのDateTimeFormatterを使用してください。構文解析の面では少し厳密なので、SimpleDateFormatの代わりになるものはそれほど多くありませんが、joda-timeは、安全性とパフォーマンスの点で、はるかに同時に使いやすいです。


3

私は、parse()とformat()へのアクセスを同期化し、ドロップイン置換として使用できるSimpleDateFormatの単純なラッパークラスを作成します。オプション#2よりも簡単で、オプション#3よりも扱いやすくなっています。

SimpleDateFormatを非同期にすることは、Java APIデザイナー側の設計上の決定としては不十分であるように思われます。だれもがformat()とparse()を同期する必要があるとは思っていません。


1

別のオプションは、インスタンスをスレッドセーフなキューに保持することです。

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インスタンスが作成されます。これはエラーを生成せず、一度だけ使用されるSimpleDateFormatを作成するペナルティが発生するだけです。


1

オプション3でこれを実装しましたが、いくつかのコードを変更しました。

  • ThreadLocalは通常静的でなければなりません
  • (get()== null)かどうかをテストするのではなく、initialValue()をオーバーライドするほうがきれいに見えます。
  • デフォルト設定が本当に必要でない限り、ロケールとタイムゾーンを設定することをお勧めします(デフォルトは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);
    }

0

アプリケーションに1つのスレッドがあるとします。では、なぜSimpleDataFormat変数へのアクセスを同期するのでしょうか。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.