2016-04-22 2 views
3

Недавно я понял/узнал, что у SimpleDateFormat есть серьезные проблемы, и поскольку Java 8 больше не нужно использовать. Я имею в виду ... Я как бы знал это, но никогда не обращал на это особого внимания. Все идет нормально.SimpleDateFormat - небезопасно, но почему именно?

Но тогда ОК ... У меня есть много устаревшего кода, написанного за последние 7-8 лет, который использует SimpleDateFormat, хранит много объектов SimpleDateFormat как статические поля и использует их для синтаксического анализа/форматирования дат. И на самом деле у меня никогда не было проблем с этими SimpleDateFormat экземплярами (статическими или нет) в производстве (за все эти годы).

Итак ... Я хочу просмотреть и проанализировать этот устаревший код и посмотреть, действительно ли в нем есть опасное использование SimpleDateFormat.

Поэтому мой вопрос ...

При каких сценариях именно SimpleDateFormat проблематично использовать?
Могу ли я получить какой-то контрольный список, чтобы я просмотрел свой старый код и посмотрел, есть ли какой-либо из моих сценариев в списке «попробуйте избежать»?

+1

Это не поточно, так если два потока используют один и тот же объект SimpleDateFormat, в то же время возникают проблемы. – Henry

+0

@ Хенри Да, я слышал так ... Но ОК ... это единственная проблема с этим? –

+1

Если код в настоящее время работает, добавляются новые дефекты. Однако новый API Date & Time ** намного удобнее в использовании. –

ответ

1

не является потокобезопасным, поэтому в каждом многопоточном сценарии (например, в веб-приложении) вы не можете объявить один форматировщик в классе констант и использовать его вокруг своих бизнес-методов, не рискуя странными ошибками ... прежде всего, когда вы начните играть с часовыми поясами и т. д.

3

SimpleDateFormat не является потоковым, любой сценарий, в который вы помещаете его в поле, к которому обращаются несколько потоков, будет потенциальной проблемой. Он не взорвется на вас, но может привести к генерированию неправильных результатов.

Триединые случаи этого были бы вопросом проверки того, для чего вы используете результаты, и насколько важно, чтобы эти значения всегда были правильными. Кроме того, очевидно, что чем больше потоков вы используете, тем больше вероятность ошибок, поэтому приложения, которые многопоточны, но которые редко имеют более одного пользователя, могут быть приоритетными вниз.

2

Одна из проблем, например, состоит в том, что SimpleDateFormat имеет внутреннее поле, установленное на дату/календарь, который вы хотите отформатировать.

Так что если у вас есть два потока, использующих один и тот же SDF одновременно с двумя разными датами, форматированная дата может изменяться прямо в середине форматирования, в результате чего создается строка, представляющая собой смесь между двумя датами.

Вот что иллюстрирует пример ниже. Если вы запустите его с помощью ExecutorService es = Executors.newFixedThreadPool(1); (однопоточное), результирующий набор будет иметь две даты, как и ожидалось. Если вместо этого вы используете (многопоточное), вполне вероятно, что в результирующем наборе будет больше дат, которые представляют собой сочетание двух дат.

К примеру, на моей машине выходы:

  • однопоточный (ожидаемый результат):

    [02-Jan-1970 4:46:40, 01-январе- 1970 1:00:00]

  • поддерживает многопоточность:

    [02-Jan-1970 01:00:00, 01-янв-1970 04:00:00, 02-янв-1970 01:00:40, 01-янв-1970 01:00:40, 02-янв. -1970 04:46:00, 02-янв-1970 01:46:00, 01-янв-1970 01:00:00, 02-янв-1970 04:00:00, 02-янв-1970 01:46: 40, 02-янв-1970 04:00:40, 01-янв-1970 01:46:00, 01-янв-1970 01:46:40, 02-янв-1970 04:46:40, 01-янв- 1970 4:00:40, 01-Jan-1970 4:46:40, 01-Jan-1970 4:46:00]


private static final DateFormat FMT = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss"); 
private static final CountDownLatch LATCH = new CountDownLatch(1); 

public static void main(String[] args) throws Exception { 
    ExecutorService es = Executors.newFixedThreadPool(1); 
    Date d1 = new Date(0); 
    Date d2 = new Date(100_000_000); 
    List<Future<String>> futures = new ArrayList<>(); 
    for (int i = 0; i < 10_000; i++) { 
    Date d = i % 2 == 0 ? d1 : d2; 
    Future<String> f = es.submit(() -> run(d)); 
    futures.add(f); 
    } 
    LATCH.countDown(); 
    es.shutdown(); 
    es.awaitTermination(5, TimeUnit.SECONDS); 
    Set<String> results = new HashSet<>(); 
    for (Future<String> f : futures) { 
    results.add(f.get()); 
    } 

    System.out.println(results); 
} 

private static String run(Date d) throws InterruptedException { 
    LATCH.await(); 
    return FMT.format(d); 
} 
Смежные вопросы