94

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

9 に答える 9

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

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

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

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

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

于 2010-11-05T16:41:41.827 に答える
25

もう1つのオプションはCommonsLangFastDateFormatですが、これは日付のフォーマットにのみ使用でき、解析には使用できません。

Jodaとは異なり、フォーマットのドロップイン置換として機能できます。(更新:v3.3.2以降、FastDateFormatはFastDateParserを生成できますこれは、SimpleDateFormatのドロップインスレッドセーフな代替品です)

于 2011-03-03T18:47:50.370 に答える
20

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);
于 2014-10-23T16:54:59.567 に答える
6

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

于 2014-03-19T18:24:01.257 に答える
4

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

于 2010-11-07T23:55:50.190 に答える
3

つまり、parse()とformat()へのアクセスを同期し、ドロップインの代替として使用できるSimpleDateFormatの単純なラッパークラスを作成します。オプション#2よりも確実で、オプション#3よりも面倒ではありません。

SimpleDateFormatを非同期にすることは、JavaAPI設計者の設計上の決定としては不十分だったようです。format()とparse()を同期する必要があると誰もが期待しているとは思えません。

于 2013-12-09T17:45:01.363 に答える
1

もう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インスタンスが作成されますが、これはいっぱいであるため、dateFormatQueueに戻すことができません。これはエラーを生成しません。一度だけ使用されるSimpleDateFormatを作成するというペナルティが発生するだけです。

于 2014-05-22T11:53:04.523 に答える
1

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

  • ThreadLocalは通常静的である必要があります
  • if(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);
    }
    
于 2016-02-23T07:26:31.980 に答える
-1

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

于 2014-02-23T03:03:14.040 に答える