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