Semua orang memperingatkan tentang Java DateFormat tidak aman dan saya mengerti konsepnya secara teoritis.
Tetapi saya tidak dapat memvisualisasikan masalah aktual apa yang dapat kita hadapi karena ini. Katakanlah, saya memiliki bidang DateFormat di kelas dan hal yang sama digunakan dalam metode yang berbeda di kelas (memformat tanggal) di lingkungan multi-utas.
Apakah ini akan menyebabkan:
- pengecualian seperti pengecualian format
- perbedaan dalam data
- ada masalah lain?
Juga, tolong jelaskan mengapa.
java
multithreading
date-format
haps10
sumber
sumber
Jawaban:
Mari kita coba.
Berikut adalah program di mana banyak utas menggunakan yang dibagikan
SimpleDateFormat
.Program :
Jalankan ini beberapa kali dan Anda akan melihat:
Pengecualian :
Berikut ini beberapa contoh:
1.
2.
3.
Hasil yang salah :
Hasil yang benar :
Pendekatan lain untuk menggunakan DateFormats dengan aman di lingkungan multi-utas adalah menggunakan
ThreadLocal
variabel untuk memegangDateFormat
objek, yang berarti bahwa setiap utas akan memiliki salinannya sendiri dan tidak perlu menunggu utas lain untuk melepaskannya. Begini caranya:Ini adalah posting yang bagus dengan detail lebih lanjut.
sumber
Saya mengharapkan korupsi data - mis. Jika Anda menguraikan dua tanggal pada saat yang sama, Anda dapat membuat satu panggilan terpolusi oleh data dari yang lain.
Sangat mudah untuk membayangkan bagaimana ini bisa terjadi: parsing sering melibatkan mempertahankan sejumlah keadaan seperti apa yang telah Anda baca sejauh ini. Jika dua utas sama-sama menginjak-injak pada kondisi yang sama, Anda akan mendapatkan masalah. Misalnya,
DateFormat
memperlihatkancalendar
bidang jenisCalendar
, dan melihat kodeSimpleDateFormat
, beberapa metode panggilancalendar.set(...)
dan panggilan laincalendar.get(...)
. Ini jelas bukan thread-safe.Saya belum melihat ke dalam tepat rincian mengapa
DateFormat
tidak thread-safe, tapi bagi saya itu cukup untuk mengetahui bahwa itu adalah aman tanpa sinkronisasi - sopan santun yang tepat dari non-keamanan bahkan bisa mengubah antara rilis.Secara pribadi saya akan menggunakan parser dari Joda Waktu bukan, karena mereka adalah thread aman - dan Joda Waktu adalah tanggal dan waktu yang lebih baik banyak API untuk memulai dengan :)
sumber
Jika Anda menggunakan Java 8 maka Anda dapat menggunakan
DateTimeFormatter
.Kode:
Keluaran:
sumber
Secara kasar, Anda tidak boleh mendefinisikan
DateFormat
variabel instance dari objek yang diakses oleh banyak utas, ataustatic
.Jadi, jika Anda
Foo.handleBar(..)
diakses oleh banyak utas, alih-alih:kamu harus menggunakan:
Juga, dalam semua kasus, tidak punya
static
DateFormat
Seperti dicatat oleh Jon Skeet, Anda dapat memiliki variabel instance statis dan variabel bersama jika Anda melakukan sinkronisasi eksternal (mis. Gunakan
synchronized
sekitar panggilan keDateFormat
)sumber
SimpleDateFormat
sangat sering. Itu akan tergantung pada pola penggunaan.Ini berarti misalkan Anda memiliki objek DateFormat dan Anda mengakses objek yang sama dari dua utas yang berbeda dan Anda memanggil metode format pada objek yang kedua utas tersebut akan masukkan pada metode yang sama pada waktu yang sama pada objek yang sama sehingga Anda dapat memvisualisasikannya memenangkan akan menghasilkan hasil yang tepat
Jika Anda harus bekerja dengan DateFormat, maka Anda harus melakukan sesuatu
sumber
Data rusak. Kemarin saya perhatikan di program multithread saya di mana saya punya
DateFormat
objek statis dan memanggilformat()
nilai-nilainya untuk dibaca melalui JDBC. Saya memiliki pernyataan pilih SQL di mana saya membaca tanggal yang sama dengan nama yang berbeda (SELECT date_from, date_from AS date_from1 ...
). Pernyataan seperti itu digunakan dalam 5 utas untuk berbagai tanggal dalamWHERE
petunjuk. Tanggal tampak "normal" tetapi nilainya berbeda - sementara semua tanggal berasal dari tahun yang sama hanya bulan dan hari yang berubah.Jawaban lain menunjukkan cara untuk menghindari korupsi seperti itu. Saya membuat saya
DateFormat
tidak statis, sekarang adalah anggota kelas yang memanggil pernyataan SQL. Saya juga menguji versi statis dengan sinkronisasi. Keduanya bekerja dengan baik tanpa perbedaan kinerja.sumber
Spesifikasi Format, NumberFormat, DateFormat, MessageFormat, dll. Tidak dirancang untuk aman utas. Juga, metode parse memanggil
Calendar.clone()
metode dan itu mempengaruhi jejak kalender sehingga banyak urutan penguraian secara bersamaan akan mengubah kloning instance Kalender.Untuk lebih lanjut, ini adalah laporan bug seperti ini dan ini , dengan hasil masalah keamanan thread DateFormat.
sumber
Dalam jawaban terbaik, dogbane memberi contoh penggunaan
parse
fungsi dan apa tujuannya. Di bawah ini adalah kode yang memungkinkan Anda memeriksaformat
fungsi.Perhatikan bahwa jika Anda mengubah jumlah pelaksana (utas bersamaan) Anda akan mendapatkan hasil yang berbeda. Dari eksperimen saya:
newFixedThreadPool
diatur ke 5 dan loop akan gagal setiap saat.Saya menduga YMMV tergantung pada prosesor Anda.
The
format
Fungsi gagal dengan memformat waktu dari benang yang berbeda. Ini karenaformat
fungsi internal menggunakancalendar
objek yang diatur pada awalformat
fungsi. Dancalendar
objeknya adalah propertiSimpleDateFormat
kelas. Mendesah...sumber
Jika ada beberapa utas yang memanipulasi / mengakses instance DateFormat tunggal dan sinkronisasi tidak digunakan, dimungkinkan untuk mendapatkan hasil yang diacak. Itu karena beberapa operasi non-atom dapat mengubah keadaan atau melihat memori tidak konsisten.
sumber
Ini adalah kode sederhana saya yang menunjukkan DateFormat tidak aman.
Karena semua utas menggunakan objek SimpleDateFormat yang sama, ia melempar pengecualian berikut.
Tetapi jika kita melewatkan objek berbeda ke utas berbeda, kode tersebut berjalan tanpa kesalahan.
Inilah hasilnya.
sumber
Ini akan menyebabkan
ArrayIndexOutOfBoundsException
Terlepas dari hasil yang salah, itu akan memberi Anda crash dari waktu ke waktu. Itu tergantung pada kecepatan mesin Anda; di laptop saya, itu terjadi sekali dalam 100.000 panggilan rata-rata:
baris terakhir dapat memicu pengecualian eksekutor yang ditunda:
sumber