[java] SimpleDateFormat에 대한 액세스 동기화

SimpleDateFormat에 대한 javadoc은 SimpleDateFormat이 동기화되지 않음을 나타냅니다.

“날짜 형식은 동기화되지 않습니다. 각 스레드에 대해 별도의 형식 인스턴스를 만드는 것이 좋습니다. 여러 스레드가 동시에 형식에 액세스하는 경우 외부에서 동기화해야합니다.”

그러나 다중 스레드 환경에서 SimpleDateFormat의 인스턴스를 사용하는 가장 좋은 방법은 무엇입니까? 내가 생각한 몇 가지 옵션이 있습니다. 과거에 옵션 1과 2를 사용했지만 더 나은 대안이 있는지 또는 이러한 옵션 중 어떤 옵션이 최상의 성능과 동시성을 제공하는지 알고 싶습니다.

옵션 1 : 필요한 경우 로컬 인스턴스 생성

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

옵션 2 : SimpleDateFormat의 인스턴스를 클래스 변수로 생성하지만 이에 대한 액세스를 동기화합니다.

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

옵션 3 : ThreadLocal을 생성하여 각 스레드에 대해 SimpleDateFormat의 다른 인스턴스를 저장합니다.

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



답변

  1. SimpleDateFormat을 만드는 것은 비용많이 듭니다 . 드물게 사용하지 않는 한 이것을 사용하지 마십시오.

  2. 당신이 약간의 차단으로 살 수 있다면 좋습니다. formatDate ()가 많이 사용되지 않는 경우 사용하십시오.

  3. 스레드를 재사용하는 경우 가장 빠른 옵션 ( 스레드 풀 ). 2보다 많은 메모리를 사용하고 시작 오버 헤드가 더 높습니다.

응용 프로그램의 경우 2.와 3. 모두 실행 가능한 옵션입니다. 귀하의 사례에 가장 적합한 것은 사용 사례에 따라 다릅니다. 조기 최적화에주의하십시오. 이것이 문제라고 생각하는 경우에만 수행하십시오.

타사에서 사용할 라이브러리의 경우 옵션 3을 사용합니다.


답변

다른 옵션은 Commons Lang FastDateFormat 이지만 파싱이 아닌 날짜 형식에만 사용할 수 있습니다.

Joda와 달리 서식 지정을위한 드롭 인 대체 기능을 수행 할 수 있습니다. (업데이트 : V3.3.2 때문에 FastDateFormat는 생산할 수 FastDateParser 드롭 인 SimpleDateFormat에 대한 스레드 안전 여분이다)


답변

Java 8을 사용하는 경우 java.time.format.DateTimeFormatter다음 을 사용할 수 있습니다 .

이 클래스는 변경 불가능하며 스레드로부터 안전합니다.

예 :

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


답변

Commons Lang 3.x에는 이제 FastDateParser와 FastDateFormat이 있습니다. 스레드로부터 안전하고 SimpleDateFormat보다 빠릅니다. 또한 SimpleDateFormat과 동일한 형식 / 파싱 패턴 사양을 사용합니다.


답변

SimpleDateFormat을 사용하지 말고 joda-time의 DateTimeFormatter를 대신 사용하십시오. 파싱 ​​측면에서 약간 더 엄격하므로 SimpleDateFormat의 대체물이 크게 떨어지지는 않지만 joda-time은 안전성과 성능면에서 훨씬 더 친숙합니다.


답변

parse () 및 format ()에 대한 액세스를 동기화하고 드롭 인 대체로 사용할 수있는 SimpleDateFormat에 대한 간단한 래퍼 클래스를 만듭니다. 옵션 # 2보다 더 안전하고 옵션 # 3보다 덜 번거 롭습니다.

SimpleDateFormat을 동기화되지 않게 만드는 것은 Java API 디자이너의 잘못된 디자인 결정 인 것처럼 보입니다. 누구든지 format () 및 parse ()가 동기화되어야한다고 예상하지 않습니다.


답변

또 다른 옵션은 스레드로부터 안전한 큐에 인스턴스를 유지하는 것입니다.

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의 크기는이 함수를 동시에 호출 할 수있는 예상 스레드 수에 가까운 크기 여야합니다. 이 수보다 많은 스레드가 실제로 모든 인스턴스를 동시에 사용하는 최악의 경우, 일부 SimpleDateFormat 인스턴스가 생성되며,이 인스턴스는 가득 차서 dateFormatQueue로 반환 될 수 없습니다. 이것은 오류를 생성하지 않으며 한 번만 사용되는 SimpleDateFormat을 생성하는 페널티가 발생합니다.