マルチスレッド―日付

### 前言
通常の作業では、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がマルチスレッドで動作するのは安全ではありません。