Javadoc untuk SimpleDateFormat menyatakan bahwa SimpleDateFormat tidak disinkronkan.
"Format tanggal tidak disinkronkan. Disarankan untuk membuat contoh format terpisah untuk setiap utas. Jika beberapa utas mengakses format secara bersamaan, itu harus disinkronkan secara eksternal."
Tapi apa pendekatan terbaik untuk menggunakan instance SimpleDateFormat di lingkungan multi threaded. Berikut adalah beberapa opsi yang telah saya pikirkan, saya telah menggunakan opsi 1 dan 2 di masa lalu, tetapi saya ingin tahu apakah ada alternatif yang lebih baik atau opsi mana yang menawarkan kinerja dan konkurensi terbaik.
Opsi 1: Buat instance lokal saat diperlukan
public String formatDate(Date d) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(d);
}
Opsi 2: Buat instance SimpleDateFormat sebagai variabel kelas tetapi akses sinkronisasinya.
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
public String formatDate(Date d) {
synchronized(sdf) {
return sdf.format(d);
}
}
Opsi 3: Buat ThreadLocal untuk menyimpan instance SimpleDateFormat yang berbeda untuk setiap utas.
private ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>();
public String formatDate(Date d) {
SimpleDateFormat sdf = tl.get();
if(sdf == null) {
sdf = new SimpleDateFormat("yyyy-MM-hh");
tl.set(sdf);
}
return sdf.format(d);
}
Jawaban:
Membuat SimpleDateFormat itu mahal . Jangan gunakan ini kecuali jarang dilakukan.
OK jika Anda bisa hidup dengan sedikit pemblokiran. Gunakan jika formatDate () tidak banyak digunakan.
Opsi tercepat JIKA Anda menggunakan kembali utas ( kumpulan utas ). Menggunakan lebih banyak memori daripada 2. dan memiliki overhead startup yang lebih tinggi.
Untuk aplikasi, 2. dan 3. adalah opsi yang memungkinkan. Yang terbaik untuk kasus Anda tergantung pada kasus penggunaan Anda. Waspadai pengoptimalan prematur. Lakukan hanya jika Anda yakin ini merupakan masalah.
Untuk perpustakaan yang akan digunakan oleh pihak ke-3, saya akan menggunakan opsi 3.
sumber
SimpleDateFormat
sebagai variabel instan, maka kita dapat menggunakannyasynchronized block
untuk membuatnya aman bagi thread. Tapi sonar menunjukkan peringatan cumi-cumi-AS2885 . Apakah ada cara untuk mengatasi masalah sonar?Opsi lainnya adalah Commons Lang FastDateFormat tetapi Anda hanya dapat menggunakannya untuk pemformatan tanggal dan bukan parsing.
Tidak seperti Joda, ini dapat berfungsi sebagai pengganti drop-in untuk pemformatan. (Pembaruan: Sejak v3.3.2, FastDateFormat dapat menghasilkan FastDateParser , yang merupakan pengganti thread-safe untuk SimpleDateFormat)
sumber
FastDateFormat
memilikiparse()
metode jugaJika Anda menggunakan Java 8, Anda mungkin ingin menggunakan
java.time.format.DateTimeFormatter
:misalnya:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); String str = new java.util.Date().toInstant() .atZone(ZoneId.systemDefault()) .format(formatter);
sumber
Commons Lang 3.x sekarang memiliki FastDateParser serta FastDateFormat. Ini aman untuk benang dan lebih cepat dari SimpleDateFormat. Ini juga menggunakan spesifikasi pola format / parse yang sama seperti SimpleDateFormat.
sumber
Jangan gunakan SimpleDateFormat, gunakan DateTimeFormatter joda-time sebagai gantinya. Ini sedikit lebih ketat di sisi penguraian dan jadi bukan penurunan yang cukup untuk menggantikan SimpleDateFormat, tetapi joda-time jauh lebih ramah secara bersamaan dalam hal keamanan dan kinerja.
sumber
Saya akan mengatakan, buat kelas pembungkus sederhana untuk SimpleDateFormat yang menyinkronkan akses ke parse () dan format () dan dapat digunakan sebagai pengganti drop-in. Lebih aman daripada opsi # 2 Anda, lebih praktis daripada opsi # 3 Anda.
Sepertinya membuat SimpleDateFormat tidak tersinkronisasi adalah keputusan desain yang buruk di pihak desainer Java API; Saya ragu ada yang mengharapkan format () dan parse () perlu disinkronkan.
sumber
Opsi lainnya adalah menyimpan instance dalam antrian thread-safe:
import java.util.concurrent.ArrayBlockingQueue; private static final int DATE_FORMAT_QUEUE_LEN = 4; private static final String DATE_PATTERN = "yyyy-MM-dd HH:mm:ss"; private ArrayBlockingQueue<SimpleDateFormat> dateFormatQueue = new ArrayBlockingQueue<SimpleDateFormat>(DATE_FORMAT_QUEUE_LEN); // thread-safe date time formatting public String format(Date date) { SimpleDateFormat fmt = dateFormatQueue.poll(); if (fmt == null) { fmt = new SimpleDateFormat(DATE_PATTERN); } String text = fmt.format(date); dateFormatQueue.offer(fmt); return text; } public Date parse(String text) throws ParseException { SimpleDateFormat fmt = dateFormatQueue.poll(); if (fmt == null) { fmt = new SimpleDateFormat(DATE_PATTERN); } Date date = null; try { date = fmt.parse(text); } finally { dateFormatQueue.offer(fmt); } return date; }
Ukuran dateFormatQueue harus mendekati perkiraan jumlah utas yang dapat memanggil fungsi ini secara rutin pada waktu yang sama. Dalam kasus terburuk di mana lebih banyak utas daripada jumlah ini benar-benar menggunakan semua contoh secara bersamaan, beberapa contoh SimpleDateFormat akan dibuat yang tidak dapat dikembalikan ke dateFormatQueue karena penuh. Ini tidak akan menghasilkan kesalahan, itu hanya akan menimbulkan hukuman karena membuat beberapa SimpleDateFormat yang hanya digunakan sekali.
sumber
Saya baru saja menerapkan ini dengan Opsi 3, tetapi membuat beberapa perubahan kode:
Anda mungkin ingin mengatur lokal dan zona waktu kecuali Anda benar-benar menginginkan pengaturan default (default sangat rawan kesalahan dengan Java)
private static final ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>() { @Override protected SimpleDateFormat initialValue() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-hh", Locale.US); sdf.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); return sdf; } }; public String formatDate(Date d) { return tl.get().format(d); }
sumber
Bayangkan aplikasi Anda memiliki satu utas. Mengapa Anda menyinkronkan akses ke variabel SimpleDataFormat?
sumber