SimpleDateFormat에 대한 액세스 동기화


91

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

10
이 문제를 제기하면 +1합니다. 너무 많은 사람들은 SimpleDateFormat이 스레드로부터 안전하다고 생각합니다 (나는 모든 곳에서 가정을 본다).
Adam Gent 2011 년

ThreadLocal 접근 방식에 대한 자세한 내용은 다음을 참조하십시오. javaspecialists.eu/archive/Issue172.html
miner49r

그리고 그 이유는 다음 질문을 참조하십시오. stackoverflow.com/questions/6840803/…
Raedwald

@ 3urdoch Option-2에서 'static'키워드를 실수로 건너 뛰었습니까?
M Faisal Hameed

답변:


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

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

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

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

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


Option-2를 사용 SimpleDateFormat하고 인스턴스 변수로 선언 synchronized block하면 스레드로부터 안전하게 만들 수 있습니다. 그러나 소나는 경고 squid-AS2885를 보여줍니다 . 소나 문제를 해결할 수있는 방법이 있습니까?
M Faisal Hameed

24

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

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


8
가공 랭 3.2 이후, FastDateFormatparse()뿐만 아니라 방법을
manuna

20

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

6

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


그것은 3.2 이상에서만 사용할 수하지 3.X
Wisteso

4

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


3

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

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


1

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

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을 생성하는 페널티가 발생합니다.


1

방금 옵션 3으로 구현했지만 몇 가지 코드를 변경했습니다.

  • ThreadLocal은 일반적으로 정적이어야합니다.
  • if (get () == null) 테스트보다는 initialValue ()를 재정의하는 것이 더 깨끗해 보입니다.
  • 기본 설정을 원하지 않는 한 로케일 및 시간대를 설정할 수 있습니다 (기본값은 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

애플리케이션에 스레드가 하나 있다고 가정 해보십시오. 그렇다면 SimpleDataFormat 변수에 대한 액세스를 동기화하는 이유는 무엇입니까?

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.