Sinkronisasi akses ke SimpleDateFormat


91

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);
}

10
1 untuk mengangkat masalah ini. Banyak orang berpikir bahwa SimpleDateFormat adalah thread safe (saya melihat asumsi di mana-mana).
Adam Gent

Untuk informasi lebih lanjut tentang pendekatan ThreadLocal, lihat: javaspecialists.eu/archive/Issue172.html
miner49r

Dan untuk alasannya, lihat pertanyaan ini: stackoverflow.com/questions/6840803/…
Raedwald

@ 3urdoch Apakah Anda melewatkan kata kunci 'statis' secara keliru dalam Opsi-2?
M Faisal Hameed

Jawaban:


43
  1. Membuat SimpleDateFormat itu mahal . Jangan gunakan ini kecuali jarang dilakukan.

  2. OK jika Anda bisa hidup dengan sedikit pemblokiran. Gunakan jika formatDate () tidak banyak digunakan.

  3. 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.


Jika kita menggunakan Option-2 dan mendeklarasikan SimpleDateFormatsebagai variabel instan, maka kita dapat menggunakannya synchronized blockuntuk membuatnya aman bagi thread. Tapi sonar menunjukkan peringatan cumi-cumi-AS2885 . Apakah ada cara untuk mengatasi masalah sonar?
M Faisal Hameed

24

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)


8
Sejak Commons Lang 3.2, FastDateFormatmemiliki parse()metode juga
manuna

20

Jika Anda menggunakan Java 8, Anda mungkin ingin menggunakan java.time.format.DateTimeFormatter:

Kelas ini tidak dapat diubah dan aman untuk thread.

misalnya:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String str = new java.util.Date().toInstant()
                                 .atZone(ZoneId.systemDefault())
                                 .format(formatter);

6

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.


Ini hanya tersedia di 3.2+ dan bukan 3.x
Wisteso

4

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.


3

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.


1

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.


1

Saya baru saja menerapkan ini dengan Opsi 3, tetapi membuat beberapa perubahan kode:

  • ThreadLocal biasanya harus statis
  • Tampaknya lebih bersih untuk mengganti initialValue () daripada menguji if (get () == null)
  • 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);
    }
    

0

Bayangkan aplikasi Anda memiliki satu utas. Mengapa Anda menyinkronkan akses ke variabel SimpleDataFormat?

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.