SimpleDateFormat'a erişimi eşitleme


91

SimpleDateFormat için javadoc, SimpleDateFormat'ın senkronize edilmediğini belirtir.

"Tarih biçimleri senkronize edilmez. Her iş parçacığı için ayrı biçim örnekleri oluşturulması önerilir. Birden çok iş parçacığı bir biçime aynı anda erişirse, dışarıdan eşitlenmelidir."

Ancak, çok iş parçacıklı bir ortamda SimpleDateFormat örneğini kullanmak için en iyi yaklaşım nedir? İşte düşündüğüm birkaç seçenek, geçmişte 1. ve 2. seçenekleri kullandım ancak daha iyi alternatifler olup olmadığını veya bu seçeneklerden hangisinin en iyi performansı ve eşzamanlılığı sunacağını merak ediyorum.

1. Seçenek: Gerektiğinde yerel örnekler oluşturun

public String formatDate(Date d) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    return sdf.format(d);
}

Seçenek 2: Sınıf değişkeni olarak SimpleDateFormat örneğini oluşturun, ancak buna erişimi senkronize edin.

private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
public String formatDate(Date d) {
    synchronized(sdf) {
        return sdf.format(d);
    }
}

Seçenek 3: Her iş parçacığı için farklı bir SimpleDateFormat örneği depolamak için bir ThreadLocal oluşturun.

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
Bu sorunu gündeme getirmek için +1. Pek çok insan SimpleDateFormat'ın iş parçacığı için güvenli olduğunu düşünüyor (her yerde varsayımlar görüyorum).
Adam Gent

ThreadLocal yaklaşımı hakkında daha fazla bilgi için, bakınız: javaspecialists.eu/archive/Issue172.html
miner49r

Ve neden için şu soruya bakın: stackoverflow.com/questions/6840803/…
Raedwald

@ 3urdoch Seçenek-2'de yanlışlıkla 'statik' anahtar kelimeyi atladınız mı?
M Faisal Hameed

Yanıtlar:


43
  1. SimpleDateFormat oluşturmak pahalıdır . Nadiren yapılmadığı sürece bunu kullanmayın.

  2. Tamam, biraz engellemeyle yaşayabilirsen. FormatDate () fazla kullanılmazsa kullanın.

  3. En hızlı seçenek İş parçacıkları yeniden kullanırsanız ( iş parçacığı havuzu ). 2'den daha fazla bellek kullanır ve daha yüksek başlangıç ​​ek yüküne sahiptir.

Uygulamalar için hem 2. hem de 3. uygun seçeneklerdir. Sizin durumunuz için hangisinin en iyisi, kullanım durumunuza bağlıdır. Erken optimizasyona dikkat edin. Sadece bunun bir sorun olduğuna inanıyorsanız yapın.

3. parti tarafından kullanılacak kütüphaneler için 3. seçeneği kullanırdım.


Option-2'yi kullanırsak ve SimpleDateFormatbir örnek değişkeni olarak bildirirsek , synchronized blockonu iş parçacığı için güvenli hale getirmek için kullanabiliriz . Ancak sonar, kalamar-AS2885 uyarısı gösteriyor . Sonar sorununu çözmenin bir yolu var mı?
M Faisal Hameed

24

Diğer seçenek Commons Lang FastDateFormat'tır, ancak bunu yalnızca tarih biçimlendirme için kullanabilirsiniz, ayrıştırma için değil.

Joda'dan farklı olarak, biçimlendirmenin yerine geçme işlevi görebilir. (Güncelleme: v3.3.2 itibaren FastDateFormat bir üretebilir FastDateParser bir damla SimpleDateFormat için evreli değiştirilmesi olduğunu)


8
Commons Lang 3.2'den FastDateFormatberi parse()yöntem de var
manuna

20

Java 8 kullanıyorsanız, aşağıdakileri kullanmak isteyebilirsiniz java.time.format.DateTimeFormatter:

Bu sınıf değişmez ve iş parçacığı açısından güvenlidir.

Örneğin:

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

6

Commons Lang 3.x artık FastDateParser ve FastDateFormat'a sahiptir. İş parçacığı için güvenlidir ve SimpleDateFormat'tan daha hızlıdır. Ayrıca SimpleDateFormat ile aynı format / ayrıştırma kalıbı spesifikasyonlarını kullanır.



4

SimpleDateFormat kullanmayın, bunun yerine joda-time's DateTimeFormatter kullanın. Ayrıştırma tarafında biraz daha katıdır ve bu nedenle SimpleDateFormat'ın yerini almada pek bir düşüş değildir, ancak joda-time, güvenlik ve performans açısından çok daha eşzamanlı dostudur.


3

SimpleDateFormat için parse () ve format () erişimini senkronize eden ve drop-in ikamesi olarak kullanılabilen basit bir sarmalayıcı sınıfı oluşturun diyebilirim. 2. seçeneğinizden daha kusursuz, 3. seçeneğinizden daha az külfetli.

Görünüşe göre SimpleDateFormat'ı senkronize edilmemiş yapmak, Java API tasarımcıları açısından zayıf bir tasarım kararıdır; Format () ve parse () öğelerinin eşitlenmesini beklediğinden şüpheliyim.


1

Diğer bir seçenek, örnekleri iş parçacığı açısından güvenli bir kuyrukta tutmaktır:

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

DateFormatQueue boyutu, bu işlevi aynı anda rutin olarak çağırabilen tahmini iş parçacığı sayısına yakın bir şey olmalıdır. Bu sayıdan daha fazla iş parçacığının aslında tüm örnekleri aynı anda kullandığı en kötü durumda, dolu olduğu için dateFormatQueue'ya döndürülemeyen bazı SimpleDateFormat örnekleri oluşturulacaktır. Bu bir hata oluşturmaz, yalnızca bir kez kullanılan bazı SimpleDateFormat öğelerini oluşturma cezasına neden olur.


1

Bunu Seçenek 3 ile uyguladım, ancak birkaç kod değişikliği yaptım:

  • ThreadLocal genellikle statik olmalıdır
  • Test if (get () == null) yerine initialValue () değerini geçersiz kılmak daha temiz görünüyor
  • Varsayılan ayarları gerçekten istemediğiniz sürece yerel ayarı ve saat dilimini ayarlamak isteyebilirsiniz (varsayılanlar Java'da hataya çok açıktır)

    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

Uygulamanızda bir iş parçacığı olduğunu hayal edin. O halde neden SimpleDataFormat değişkenine erişimi senkronize edersiniz?

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.