simpledateformat スレッドが安全でないのはなぜですか?

青灯夜游
リリース: 2023-01-13 00:39:56
オリジナル
17248 人が閲覧しました

原因: マルチスレッド環境では、format メソッドの呼び出しなど、複数のスレッドが同じ SimpleDateFormat オブジェクト (静的変更など) を同時に使用すると、複数のスレッドが calender.setTime メソッドを呼び出します。同時に、他のスレッドによって変更される時間が発生するため、スレッドは安全ではありません。

simpledateformat スレッドが安全でないのはなぜですか?

#このチュートリアルの動作環境: Windows7 システム、Java8 バージョン、DELL G3 コンピューター。

スレッドの安全性の確認:

/**
 * SimpleDateFormat线程安全测试
 * 〈功能详细描述〉
 *
 * @author 17090889
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
public class SimpleDateFormatTest {
    private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 100, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(1000), new MyThreadFactory("SimpleDateFormatTest"));

    public void test() {
        while (true) {
            poolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    String dateString = simpleDateFormat.format(new Date());
                    try {
                        Date parseDate = simpleDateFormat.parse(dateString);
                        String dateString2 = simpleDateFormat.format(parseDate);
                        System.out.println(dateString.equals(dateString2));
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
ログイン後にコピー

出力:

  true
  false
  true
  true
  false
ログイン後にコピー

false が表示され、スレッドが安全でないことを示します

1、形式メソッド

public StringBuffer format(Date date, StringBuffer toAppendTo,
                               FieldPosition pos)
    {
        pos.beginIndex = pos.endIndex = 0;
        return format(date, toAppendTo, pos.getFieldDelegate());
    }

    // Called from Format after creating a FieldDelegate
    private StringBuffer format(Date date, StringBuffer toAppendTo,
                                FieldDelegate delegate) {
        // Convert input date to time field list
        calendar.setTime(date);

        boolean useDateFormatSymbols = useDateFormatSymbols();

        for (int i = 0; i < compiledPattern.length; ) {
            int tag = compiledPattern[i] >>> 8;
            int count = compiledPattern[i++] & 0xff;
            if (count == 255) {
                count = compiledPattern[i++] << 16;
                count |= compiledPattern[i++];
            }

            switch (tag) {
            case TAG_QUOTE_ASCII_CHAR:
                toAppendTo.append((char)count);
                break;

            case TAG_QUOTE_CHARS:
                toAppendTo.append(compiledPattern, i, count);
                i += count;
                break;

            default:
                subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
                break;
            }
        }
        return toAppendTo;
    }
ログイン後にコピー
 protected Calendar calendar;
ログイン後にコピー

変数カレンダーが複数のスレッド間で共有され、カレンダーが変更されていることがわかります。したがって、マルチスレッド環境では、format メソッドの呼び出しなど、複数のスレッドが同じ SimpleDateFormat オブジェクト (静的変更など) を同時に使用すると、複数のスレッドが同時に calender.setTime メソッドを呼び出し、他のスレッドによって変更される可能性があるため、スレッドは安全ではありません。

さらに、parse メソッドもスレッドアンセーフです。parse メソッドは、実際には、解析のために CalenderBuilder の確立を呼び出します。メソッドの主なステップは、アトミックな操作ではありません。

解決策:

1. SimpleDateFormat をローカル変数として定義します

2. スレッド同期ロックを追加します: synchronized(lock)

3. ThreadLocal を使用すると、各スレッドは SimpleDateFormat オブジェクトの独自のコピーを持ちます。例:

/**
 * SimpleDateFormat线程安全测试
 * 〈功能详细描述〉
 *
 * @author 17090889
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
public class SimpleDateFormatTest {
        private static final ThreadLocal<SimpleDateFormat> THREAD_LOCAL = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };
    //    private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 100, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(1000), new MyThreadFactory("SimpleDateFormatTest"));

    public void test() {
        while (true) {
            poolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    SimpleDateFormat simpleDateFormat = THREAD_LOCAL.get();
                    if (simpleDateFormat == null) {
                        simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    }
                    String dateString = simpleDateFormat.format(new Date());
                    try {
                        Date parseDate = simpleDateFormat.parse(dateString);
                        String dateString2 = simpleDateFormat.format(parseDate);
                        System.out.println(dateString.equals(dateString2));
                    } catch (ParseException e) {
                        e.printStackTrace();
                    } finally {
                        local.remove();
                    }
                }
            });
        }
    }
}
ログイン後にコピー

4. SimpleDateFormat の代わりに DateTimeFormatter を使用します

DateTimeFormatter はスレッドセーフであり、デフォルトで多くの書式設定メソッドを提供します。カスタムの書式設定メソッドは ofPattern メソッドを通じて作成することもできます。

(1) 日付のフォーマット例:

 LocalDateTime localDateTime = LocalDateTime.now();
 System.out.println(localDateTime); // 2019-11-20T15:04:29.017
 DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
 String strDate=localDateTime.format(dtf);
 System.out.println(strDate); // 2019/23/20 15:23:46
ログイン後にコピー

(2) 解析日付

 DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
 LocalDateTime localDateTime=LocalDateTime.parse("2019/11/20 15:23:46",dtf);
 System.out.println(localDateTime); // 2019-11-20T15:23:46
ログイン後にコピー

関連ビデオチュートリアルの推奨事項:

Java ビデオ チュートリアル

以上がsimpledateformat スレッドが安全でないのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート