JAvaマルチスレッドのDateTimeFormatterとSimpleDateFormat
参照先:https://www.jianshu.com/p/b212afa16f1f
SimpleDateFormatをstaticメンバー変数として定義すると、複数のthread間でこのSimpleDateFormatオブジェクトが共有されるため、Calendarオブジェクトも共有されます.
DateFormat.java
SimpleDateFormat.java
1.1エラーコード参照の再現
}
2.1 SimpleDateFormatをローカル変数として定義します.
欠点:メソッドを呼び出すたびにSimpleDateFormatオブジェクトが作成され、メソッドが終了するとゴミ回収になります.2.2スレッド同期ロックを追加:synchronized(lock)
欠点:パフォーマンスが劣っており、ロックが解放されるたびに他のスレッドが2.3に入るのを待つ必要があります.ThreadLocalを使用すると、各スレッドに独自のSimpleDateFormatオブジェクトのコピーがあります.
3.SimpleDateFormatコードの代わりにDateTimeFormatterを使用するDateTimeFormatterTestを参照する.java jdk1.8には、日付処理方法としてLocalDateやLocalDateTimeなどのクラスが追加され、日付フォーマットの問題を解決するための新しいクラスDateTimeFormatterが導入されました.LocalDateTime、DateTimeFormatterの2つのクラスにはスレッドの問題はありません.共有変数として作成しない限り、スレッドの問題はありません.Dateの代わりにInstant、Calendarの代わりにLocalDateTime、SimpleDateFormatの代わりにDateTimeFormatterを使用できます.
DateTimeFormatterを使用してフォーマットを完了
DateTimeFormatterを使用して文字列を解析する
1.SimpleDateFormatはなぜスレッドが安全ではないのですか?
SimpleDateFormatをstaticメンバー変数として定義すると、複数のthread間でこのSimpleDateFormatオブジェクトが共有されるため、Calendarオブジェクトも共有されます.
public static SimpleDateFormat formater = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
System.out.println(formater.format(new Date())+" Exception made...");
DateFormat.java
public final String format(Date date)
{
return format(date, new StringBuffer(),
DontCareFieldPosition.INSTANCE).toString();
}
public abstract StringBuffer format(Date date, StringBuffer toAppendTo,
FieldPosition fieldPosition);
SimpleDateFormat.java
@Override
public StringBuffer format(Date date, StringBuffer toAppendTo,
FieldPosition pos)
{
// ,
// pos ,
pos.beginIndex = pos.endIndex = 0;
return format(date, toAppendTo, pos.getFieldDelegate());
}
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;
}
1.1エラーコード参照の再現
public class DateFormatTest {
private static SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy", Locale.US);
private static String date[] = { "01-Jan-1999", "09-Jan-2000", "08-Jan-2001" , "07-Jan-2002" , "06-Jan-2003" , "05-Jan-2004" , "04-Jan-2005" , "03-Jan-2006" , "02-Jan-2007" };
public static void main(String[] args) {
for (int i = 0; i < date.length; i++) {
final int temp = i;
new Thread(new Runnable() {
@Override
public void run() {
try {
while (true) {
String str1 = date[temp];
String str2 = sdf.format(sdf.parse(str1));
System.out.println(Thread.currentThread().getName() + ", " + str1 + "," + str2);
if(!str1.equals(str2)){
throw new RuntimeException(Thread.currentThread().getName()
+ ", Expected " + str1 + " but got " + str2);
}
}
} catch (Exception e) {
throw new RuntimeException("parse failed", e);
}
}
}).start();
}
}
}
2.SimpleDateFormatスレッドの不安全な解決方法
2.1 SimpleDateFormatをローカル変数として定義します.
SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy", Locale.US);
String str1 = "01-Jan-2010";
String str2 = sdf.format(sdf.parse(str1));
欠点:メソッドを呼び出すたびにSimpleDateFormatオブジェクトが作成され、メソッドが終了するとゴミ回収になります.2.2スレッド同期ロックを追加:synchronized(lock)
public class SyncDateFormatTest {
private static SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy", Locale.US);
private static String date[] = { "01-Jan-1999", "01-Jan-2000", "01-Jan-2001" };
public static void main(String[] args) {
for (int i = 0; i < date.length; i++) {
final int temp = i;
new Thread(new Runnable() {
@Override
public void run() {
try {
while (true) {
synchronized (sdf) {
String str1 = date[temp];
Date date = sdf.parse(str1);
String str2 = sdf.format(date);
System.out.println(Thread.currentThread().getName() + ", " + str1 + "," + str2);
if(!str1.equals(str2)){
throw new RuntimeException(Thread.currentThread().getName()
+ ", Expected " + str1 + " but got " + str2);
}
}
}
} catch (Exception e) {
throw new RuntimeException("parse failed", e);
}
}
}).start();
}
}
}
欠点:パフォーマンスが劣っており、ロックが解放されるたびに他のスレッドが2.3に入るのを待つ必要があります.ThreadLocalを使用すると、各スレッドに独自のSimpleDateFormatオブジェクトのコピーがあります.
public class DateUtil {
private static ThreadLocal local = new ThreadLocal();
public static Date parse(String str) throws Exception {
SimpleDateFormat sdf = local.get();
if (sdf == null) {
sdf = new SimpleDateFormat("dd-MMM-yyyy", Locale.US);
local.set(sdf);
}
return sdf.parse(str);
}
public static String format(Date date) throws Exception {
SimpleDateFormat sdf = local.get();
if (sdf == null) {
sdf = new SimpleDateFormat("dd-MMM-yyyy", Locale.US);
local.set(sdf);
}
return sdf.format(date);
}
}
public class ThreadLocalDateFormatTest {
private static String date[] = { "01-Jan-1999", "01-Jan-2000", "01-Jan-2001" };
public static void main(String[] args) {
for (int i = 0; i < date.length; i++) {
final int temp = i;
new Thread(new Runnable() {
@Override
public void run() {
try {
while (true) {
String str1 = date[temp];
Date date = DateUtil.parse(str1);
String str2 = DateUtil.format(date);
System.out.println(str1 + "," + str2);
if(!str1.equals(str2)){
throw new RuntimeException(Thread.currentThread().getName()
+ ", Expected " + str1 + " but got " + str2);
}
}
} catch (Exception e) {
throw new RuntimeException("parse failed", e);
}
}
}).start();
}
}
}
3.SimpleDateFormatコードの代わりにDateTimeFormatterを使用するDateTimeFormatterTestを参照する.java jdk1.8には、日付処理方法としてLocalDateやLocalDateTimeなどのクラスが追加され、日付フォーマットの問題を解決するための新しいクラスDateTimeFormatterが導入されました.LocalDateTime、DateTimeFormatterの2つのクラスにはスレッドの問題はありません.共有変数として作成しない限り、スレッドの問題はありません.Dateの代わりにInstant、Calendarの代わりにLocalDateTime、SimpleDateFormatの代わりにDateTimeFormatterを使用できます.
DateTimeFormatterを使用してフォーマットを完了
DateTimeFormatter[] formatters = new DateTimeFormatter[]{
// DateTimeFormatter
DateTimeFormatter.ISO_LOCAL_DATE,
DateTimeFormatter.ISO_LOCAL_TIME,
DateTimeFormatter.ISO_LOCAL_DATE_TIME,
// DateTimeFormatter
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.MEDIUM),
DateTimeFormatter.ofLocalizedTime(FormatStyle.LONG),
// DateTimeFormatter
DateTimeFormatter.ofPattern("Gyyyy%%MMM%%dd HH:mm:ss")
};
LocalDateTime date = LocalDateTime.now();
// LocalDateTime
for(int i = 0 ; i < formatters.length ; i++)
{
//
System.out.println(date.format(formatters[i]));
System.out.println(formatters[i].format(date));
}
DateTimeFormatterを使用して文字列を解析する
//
String str1 = "2014==04==12 01 06 09 ";
// 、
DateTimeFormatter fomatter1 = DateTimeFormatter
.ofPattern("yyyy==MM==dd HH mm ss ");
//
LocalDateTime dt1 = LocalDateTime.parse(str1, fomatter1);
System.out.println(dt1); // 2014-04-12T01:06:09
// --- ---
String str2 = "2014$$$ $$$13 20 ";
DateTimeFormatter fomatter2 = DateTimeFormatter
.ofPattern("yyy$$$MMM$$$dd HH ");
LocalDateTime dt2 = LocalDateTime.parse(str2, fomatter2);
System.out.println(dt2); // 2014-04-13T20:00