1

Используя этот код ниже, я заметил, что иногда дата форматируется некорректно. И чтобы сделать его еще более странным, иногда timeStamp будет иметь правильную дату, а timeStampCopy будет иметь неправильную дату и наоборот.Почему моя Java Calendar.setTime() спорадически устанавливает неправильные времена?

public static Timestamp method(String date, DateFormat dateFormat) throws Exception {   

     // date is always "2017-02-17"  

     // original 
     GregorianCalendar gCal = new GregorianCalendar(); 
     gCal.setTime(dateFormat.parse(date)); 
     Timestamp timeStamp = new Timestamp(gCal.getTimeInMillis()); 

     // copy 
     GregorianCalendar gCalCopy= new GregorianCalendar(); 
     gCalCopy.setTime(dateFormat.parse(date)); 
     Timestamp timeStampCopy = new Timestamp(gCalCopy.getTimeInMillis()); 

     if (!timeStamp.toString().contains("2017-02-17")) 
      System.out.println(timeStamp.toString()); 
     if (!timeStampCopy.toString().contains("2017-02-17")) 
      System.out.println(timeStampCopy.toString()); 

     return timeStamp; 

    } 

Я не уверен, что может быть причиной, но я попытался это с помощью объекта Date, и я с теми же вопросами. Я думал, что это может быть проблема синтаксического анализа, но поскольку он делает то же самое дважды, я не уверен.

Ниже приведены некоторые из значений, которые я получаю:

timeStamp is:  2017-02-17 00:00:00.0 
timeStampCopy is: 1700-02-17 00:00:00.0 
+0

Каковы ваши входы для функции –

+4

Вы делитесь экземпляром 'DateFormat' между потоками? –

+0

@ AndyTurner Да! Я как раз собирался добавить, что по какой-то причине, когда я добавил «synchronized» к методу, он, похоже, внезапно начал работать правильно. Не знаю, почему. – Overclock

ответ

3

Вы говорите, что вы используете экземпляр DateFormat между потоками.

Согласно Javadoc:

Дата форматы не синхронизированы. Рекомендуется создавать отдельные экземпляры формата для каждого потока. Если несколько потоков обращаются к формату одновременно, его необходимо синхронизировать извне.

Обратите внимание, что это относится к внешней синхронизации доступа к экземпляру DateFormat, не метод. Выполнение метода synchronized устранит эту проблему только в том случае, если нет другого использования экземпляра DateFormat.

Вы можете:

  • Явное синхронизировать вокруг всего кода с помощью экземпляра DateFormat (стоит добавление @GuardedBy аннотацию к переменной, для того, чтобы документ, который вы ожидаете замок, который пройдет перед использованием);
  • Измените тип переменной на ThreadLocal<DateFormat> (и соответствующим образом инициализируйте общую переменную), которая гарантирует, что каждый поток имеет свою собственную копию DateFormat.

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

Но существуют более совершенные библиотеки для обработки дат и времени, которые были разработаны с учетом таких проблем, как отсутствие безопасности резьбы DateFormat. В Java 8 существует API java.time; для более ранних версий Java существует Jodatime.

+0

Все хорошо, но последнее предложение. Проект Joda-Time теперь находится в режиме обслуживания и рекомендует переносить классы java.time. Для более ранних версий Java (6 и 7) используйте проект [ThreeTen-Backport] (http://www.threeten.org/threetenbp/). Большая часть функциональности java.time по-прежнему поддерживается. Кроме того, он адаптирован для Android в проекте [ThreeTenABP] (https://github.com/JakeWharton/ThreeTenABP). –

1

Answer by Turner является правильным и его следует принять.

java.time потокобезопасно

java.time классы решают эту проблему с помощью неизменных объектов и делает их по своей сути потокобезопасна.

LocalDate ld = LocalDate.of("2017-02-17"); 
ZoneId z = ZoneId.of("America/Montreal"); 
ZonedDateTime zdt = ld.atStartOfDay(z); 

Сформировать строку в стандартном формате ISO 8601 по телефону toString. Для других форматов используйте класс DateTimeFormatter. Поиск переполнения стека для многих примеров и обсуждений. Не беспокойтесь о потоках, все поточно-безопасные.

Для получения значения в UTC, извлеките Instant. не

Instant instant = zdt.toInstant() ; 

Нет необходимости использовать java.sql.Timestamp. Современные драйверы JDBC могут обрабатывать типы java.time с помощью методов toObject и setObject. Для более старых драйверов конвертировать с использованием новых методов, добавленных в старые классы.


О java.time

java.time каркас встроен в Java 8 и более поздних версий. Эти классы вытесняют неприятные старые legacy классы времени, такие как java.util.Date, Calendar, & SimpleDateFormat.

Проект Joda-Time, в настоящее время в maintenance mode, советует перейти на классы java.time.

Чтобы узнать больше, см. Oracle Tutorial. И поиск Stack Overflow для многих примеров и объяснений. Спецификация: JSR 310.

Где получить классы java.time?

  • Java SE 8 и SE 9, а затем
    • Встроенный.
    • Часть стандартного Java API с объединенной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и SE 7
    • Большая часть функциональности java.time будет обратно портирован на Java 6 & 7 в ThreeTen-Backport.
  • Android

Проект ThreeTen-Extra расширяет java.time с дополнительными классами. Этот проект является доказательством возможных будущих дополнений к java.time. Здесь вы можете найти полезные классы, такие как Interval, YearWeek, YearQuarter и more.

 Смежные вопросы

  • Нет связанных вопросов^_^