#java #static #calendar #thread-safety
#java #статический #Календарь #потокобезопасность
Вопрос:
Я хотел бы использовать календарь для некоторых статических методов и использовать статическое поле:
private static Calendar calendar = Calendar.getInstance();
Теперь я читаю java.util.Календарь не потокобезопасен. Как я могу сделать этот поток безопасным (он должен быть статичным)?
Комментарии:
1. Является ли ваш статический календарь просто фиксированной датой?
2. может быть, это помогло бы сделать его «изменчивым»?
3. нет, я просто не хочу создавать так много экземпляров — для каждого вызова моих методов, поскольку это, возможно, может занять время.
4. Вы имеете в виду, что вас беспокоит снижение производительности многих вызовов конструктора?
5. Я начинаю приходить к выводу, что Calendar не следует рассматривать просто как «не потокобезопасный». Он должен быть помечен как «враждебный потоку».
Ответ №1:
Вы не можете сделать что-то потокобезопасным, если это не так. В случае Calendar
даже чтение данных из него не является потокобезопасным, поскольку может обновлять внутренние структуры данных.
Если это вообще возможно, я бы предложил использовать вместо этого время Joda:
- Большинство типов являются неизменяемыми
- Неизменяемые типы потокобезопасны
- В любом случае, это, как правило, намного лучший API
Если вам абсолютно необходимо использовать Calendar
, вы могли бы создать блокирующий объект и поместить весь доступ через блокировку. Например:
private static final Calendar calendar = Calendar.getInstance();
private static final Object calendarLock = new Object();
public static int getYear()
{
synchronized(calendarLock)
{
return calendar.get(Calendar.YEAR);
}
}
// Ditto for other methods
Хотя это довольно неприятно. У вас мог бы быть только один синхронизированный метод, который создавал бы клон исходного календаря каждый раз, когда это было необходимо, конечно … возможно, что, вызвав computeFields
or computeTime
, вы могли бы сделать последующие операции чтения потокобезопасными, конечно, но лично я бы не хотел это пробовать.
Комментарии:
1. Если вы клонируете шаблон, вам не нужен синхронизированный блок. Поскольку сам шаблон неизменяем.
2. Несмотря на то, что чтение в календаре обновляет внутреннее состояние, можете ли вы привести пример, когда два потока могут попытаться установить разные значения для этого состояния?
3. @bestsss: Ну, это зависит от того, потокобезопасен ли сам clone. Учитывая отсутствие потокобезопасности в остальной части
Calendar
, я бы не хотел полагаться на это.4. @Peter: Представьте, что
areFieldsSet
становится видимым какtrue
для одного потока из-за вычислений в другом до того, как сами вычисляемые значения полей станут видимыми. Потенциально было бы возможно, чтобы один поток прочитал некоторые старые значения полей и некоторые новые, что привело бы к странным результатам. Мне не нравится пытаться рассуждать о деталях реализации, чтобы решить, возможно это или нет.5. @Voo: Мой будильник можно настроить так, чтобы он будил меня в 8 часов по местному времени каждый день, где бы я ни был. Какой часовой пояс следует использовать в его представлении? 😉
Ответ №2:
Календарь потокобезопасен при условии, что вы его не меняете. Использование в вашем примере отличное.
Стоит отметить, что Calendar не является эффективным классом, и вы должны использовать его только для сложных операций (например, для поиска следующего месяца / года) ИМХО: если вы используете его для сложных операций, используйте только локальные переменные.
Если все, что вам нужно, это моментальный снимок времени, более быстрый способ — использовать currentTimeMillis, который даже создает объект. Вы можете сделать поле изменяемым, если хотите сделать его потокобезопасным.
private static long now = System.currentTimeMillis();
Использование немного подозрительно. Зачем вам получать текущее время и сохранять его глобально вот так. Это напоминает мне старую шутку.
— У вас есть время?
— Да, у меня это где-то записано.
Комментарии:
1. Что именно вы подразумеваете под «изменить это», имея в виду, что чтение из него может изменить его внутреннее состояние? Я не был бы полностью удивлен, обнаружив, что две одновременные операции чтения могут потенциально привести к повреждению состояния. Это маловероятно, но я бы не хотел на это полагаться. Calendar — отвратительный, мерзкий класс : (
2. Его чтение не изменит его состояние непоследовательным образом. Если бы вы беспокоились об этом, вам пришлось бы сказать то же самое о String.hashCode().
3. Я согласен, что календарь отвратительный. Вы пробовали его сериализовать? Он отправляет всю информацию о часовом поясе, что не очень хорошо сказывается на производительности, но это означает, что если у вас есть система с актуальной информацией о часовом поясе, но вы читаете старый календарь или получаете календарь из системы, которая не является актуальной, вы получаете незначительные проблемы с часовым поясом, которых вы никогда не ожидали, из-за ошибок в коде, который вы не запускаете. 😛
4. Смотрите мой комментарий, отвечающий вам в моем ответе.
String
разработан таким образом, чтобы быть потокобезопасным, поэтому его хэш-кэширование потокобезопасно: значение атомарно изменяется с «некэшированного» на «кэшированное значение». Сравните это сCalendar
, гдеboolean
поле определяет, установлены ли поля, а сами поля являются отдельными. Что гарантирует, что все изменения в этом наборе данных происходят атомарно или в желаемом порядке?5. @scottb Часть того, что замедляет работу Календаря, — это создание объектов TimeZone и Locale. Если вы кэшируете эти неизменяемые объекты, вы можете ускорить создание нового календаря. Чтобы сделать календарь потокобезопасным, вам нужно заблокировать его во время использования.
Ответ №3:
Ты не можешь. Да, вы могли бы синхронизироваться с ним, но у него все еще есть изменяемые поля состояния. Вам нужно будет создать свой собственный объект календаря.
Если возможно, используйте что-нибудь легкое, например, long, измеряющий время в миллисекундах, и конвертируйте в Календарь только тогда, когда вам это нужно.
Ответ №4:
Создайте Calendar
в качестве локальной переменной в методе. Если вам нужен один и тот же календарь во всех методах, вы можете использовать статику, где (одноэлементный или квазиодноэлементный) объект был бы более подходящим.
Комментарии:
1. Вы действительно предполагаете, что создание синглтона изменяемого, не потокобезопасного объекта поможет сделать его класс потокобезопасным? Неужели?