0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

マルチスレッド―日付

Last updated at Posted at 2021-10-21

前言

通常の作業では、SimpleDateFormatという種類を使って日付変換をすることが多いですが、マルチスレッドの下では安全ではないので注意してください。

simpleDateFormat=new SimpleDateFormat(「yyy-M-dd」);

不安全コードテスト


package cn.thread.first.unsafe;

import java.text.DateFormat;

import java.text.PaseException;

import java.text.SimpleDateFormat;

import java.util.Date;

class SimpleDome extens Thread{

prvate SimpleDateFormat simpleDateFormat;

prvate String str

public SimpleDome(SimpleDateFormat sf,String str){

this.simpleDateFormat=sf;

this.str=str;

)

@オーバーライド

public void run(){

try{

Date date=simpleDateFormat.parse(str)

System.out.println(str+“=”+date);

}catch(PaseException){

e.print StockTrace();

)

)

)

public class SimpleDateFormatdome{

public static void main(String args){

//第一種類:出力結果が合わない

SimpleDateFormat simpleDateFormat=new SimpleDateFormat(「yyy-M-dd」);

String[]sts={'2017-01-10'、'2017-01-11'、'2017-01-12'、'2017-01-13'、'2017-01-14'、'2017-01-15''

Simple Dome[]domes=new SimpleDom[6];

for(int i=0;i<6;i++){

domes[i]=new SimpleDome(simpleDateFormat、sts[i])

)

for(int i=0;i<6;i++){

domes[i].start();

)

)

//第二種類:各種異常を報告する

final SimpleDateFormat simpleDateFormat 2=new SimpleDateFormat(「yyy-M-dd」);

new Thread(newRunnable){

public void run(){

String str=「2017-01-10」

Date date=new SimpleDome(simpleDateFormat 2,stToDate).

System.out.println(str+“=”+date);

)

).start();

new Thread(newRunnable){

public void run(){

String str=「2017-01-11」

Date date=new SimpleDome(simpleDateFormat 2,stToDate).

System.out.println(str+“=”+date);

)

).start();

)

1:出力結果

異常1:Exception in thread「Thread-3」java.lang.Number FormatException:For input string:「2072017 E 4」

異常2:Exception in thread「Thread-2」java.lang.Number FormatException:multiple points

異常3:変換後の日付が重なったり、合わなかったりすることがあります。

2:各種異常を報告する

異常1:Exception in thread「Thread-3」java.lang.Number FormatException:For input string:「2072017 E 4」

異常2:Exception in thread「Thread-2」java.lang.Number FormatException:multiple points

異常3:Exception in thread「Thread-3」java.lang.Number FormatException:For input string:「」

###解决案1

1.変換中にロックを入れる:

 private static final Object object=newe Object();

 @Override
    public void run() {
        synchronized(object){
            try {
                Date date = simpleDateFormat.parse(str);
                System.out.println(str + "=" + date);
            } catch (ParseException e) {
                e.printStackTrace();
            }

        }
     }

このような方式は高合併の下で、大量のスレッドがふさがり、性能に深刻な影響を与えるので、一般的にはこのように使うことを勧めません。

###解决案2

class SimpleDome 
...  
 private SimpleDateFormat simpleDateFormat= new   SimpleDateFormat("yyyy-MM-dd");

各スレッドについては、新たなsimpleDateFormatオブジェクトであり、リソース競争は存在しない。このような方式の欠陥は、同じアクセス量が多い時に、大量のsimpleDateFormatオブジェクトを作成し、変換後に捨てて、一時的にメモリを占有して、ゴミの回収にも時間がかかります。時間を消費して、空間をつぶして、更に割りに合いません。

###解决案3(推荐)

package cn.thread.first.unsafe;

import java.text.DateFormat;

import java.text.PaseException;

import java.text.SimpleDateFormat;

import java.util.Date;

class SimpleHelp{

prvate static final ThreadLocal<DateFormat>df=new ThreadLocal<DateFormat>(){

@オーバーライド

protected DateFormat initial Value(){

return new SimpleDateFormat(「yyy-M-dd」);

)

}

//書き方1、上の初期化と一緒ですよ!

public static Date convert(String source){

try{

Thread.sleep(100)

return df.get().parse(source);

}catch(Exception e){

e.print StockTrace();

)

return null

)

//書き方2

//public static DateFormat get DateFormat(){

//DateFormat dateFormat=df.get();

//if(dateFormat==null){

//dateFormat=new SimpleDateFormat(「yyy-M-dd」);

//df.set(dateFormat);

//}

//return dateFormat;

//}

//

//public static Date convert 2(String source){

//try{

//Thread.sleep(100)

//return get DateFormat().parse(source);

//}catch(Exception e){

//e.print StockTrace();

//}

//return null;

//}

)

public class SimpleDateFormatdome{

public static void main(String args){

//下はスレッドが安全です。

new Thread(newRunnable){

public void run(){

String str=「2017-01-12」

Date date=SimpleHelp.co nvert(str)

System.out.println(str+“=”+date);

)

).start();

new Thread(newRunnable){

public void run(){

String str=「2017-01-13」

Date date=SimpleHelp.co nvert(str)

System.out.println(str+“=”+date);

)

).start();

new Thread(newRunnable){

public void run(){

String str=「2017-01-14」

Date date=SimpleHelp.co nvert(str)

System.out.println(str+“=”+date);

)

).start();

)

)

正常出力結果です。

説明:ThreadLocalを使うと、スレッドごとにコピーが1つずつ保存されます。各スレッドは自分のThreadLocal->DateFormatを持っています。スレッドごとに各サブを持つDateFormatと同じです。

###SimpleDateFormatが安全でない原因?

SimpleDateFormatはDateFormatを継承しています。DateFormatではprotected属性のCalendar類の対象を定義しました。ただし、Calendar疲れの概念が複雑で、タイムゾーンやローカライズなどに関連して、Jdkの実現にメンバー変数を使ってパラメータを伝達しているため、マルチスレッドの時にエラーが発生します。

###総括
1)ローカルのDateFormatまたはSimpleDateFormatオブジェクトを使ってJavaの日付を変換またはフォーマットします。彼らをローカライズして、複数のスレッド間で共有しないようにします。

2)JavaでSimpleDateFormatクラスのためにDateを共有する場合、format()とParse()メソッドを外部で同期して呼び出す必要があります。これらはDateFormatオブジェクトの状態を変化させ、Javaで文字列をフォーマットしたり、作成日にエラーを修正することができます。DateFormat類の共有は避けたほうがいいです。

3)もし選択肢があるなら、jda-dateライブラリを使って日付と時間に関する操作を行ってください。Java Date APIを使用して、Java中のSimpleDateFormatに関連するスレッドセキュリティ問題をすべて解決します。

4)Javaの中でSimpleDateFormatのもう一つの良い代替方法は、Apachesのcommons.langバッグであり、FastDateFormatという実用類とスレッドがJavaの中のSimpleDateFormatに代わるクラスを含んでいます。

5)同期DateFormatとSimpleDateFormatのもう一つの方法はThreadLocalを使用して、それは各Threadに基づいてSimpleDateFormatを作成しますが、詳しく使用しないと、深刻なメモリ漏れの源となる可能性があります。

6)SimpleDateFormatの不安はグローバル変数Calendarを使用したことから来ています。この変数は操作中にclear、set操作をしました。-1、+1のように操作して、SimpleDateFormatがマルチスレッドで動作するのは安全ではありません。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?