У меня проблема с ограничением одновременного доступа к методу. У меня есть метод MyService
, который можно вызывать из разных мест много раз. Этот метод должен возвращать String
, который должен быть обновлен в соответствии с некоторыми правилами. Для этого у меня есть updatedString
класс. Прежде чем получить String
, он проверяет, обновлен ли String
, если нет, то обновляет его. Многие потоки могут одновременно читать String
, но ТОЛЬКО ОДИН должен обновлять String
одновременно, если он устарел.
public final class updatedString {
private static final String UPstring;
private static final Object lock = new Object();
public static String getUpdatedString(){
synchronized(lock){
if(stringNeedRenewal()){
renewString();
}
}
return getString();
}
...
Это отлично работает. Если у меня 7 потоков, получающих строку, это гарантирует, что при необходимости ТОЛЬКО один поток обновляет строку.
Мой вопрос: хорошая ли идея иметь все это static
? Почему если нет? Это быстро? Есть ли лучший способ сделать это?
Я читал такие сообщения:
В каких случаях требуется синхронный доступ к методам в Java? , что говорит о том, что статические изменяемые переменные не являются хорошей идеей, а также статическими классами. Но я не вижу никакой тупиковой ситуации в коде или лучшего решения. Только то, что некоторым потокам придется ждать обновления String
(при необходимости) или ждать, пока другой поток покинет синхронизированный блок (что вызывает небольшую задержку).
Если метод не static
, то у меня есть проблема, потому что это не будет работать, поскольку синхронизированный метод действует только для текущего экземпляра, который использует поток. Синхронизированный метод тоже не работает, похоже, что блокировка зависит от экземпляра, а не от класса.
Другим решением может быть использование Singleton, который избегает создания более одного экземпляра, а затем использует один синхронизированный нестатический класс, но мне это решение не очень нравится.
Дополнительная информация:
stringNeedRenewal()
не слишком дорого, хотя он должен читать из базы данных. renewString()
напротив, это очень дорого, и приходится читать из нескольких таблиц в базе данных, чтобы наконец прийти к ответу. String
требует произвольного обновления, но это происходит не очень часто (от одного раза в час до одного раза в неделю).
@ Форсварир заставил меня задуматься ... и я думаю, он был прав. return getString();
ДОЛЖЕН быть внутри синхронизированного метода. На первый взгляд это выглядит так, как будто это может быть вне его, поэтому потоки смогут читать его одновременно, но что произойдет, если поток прекратит работу, вызывая getString()
, а другой поток частично выполнит renewString()
? Мы могли бы иметь такую ситуацию (при условии, что один процессор):
- THREAD 1 запускается
getString()
. ОС
начинает копировать в память байты
подлежит возврату.
ОС THREAD 1 останавливается перед завершением копирования.
THREAD 2 поступает в синхронизированный
блокировка и запуск renewString()
,
изменение оригинала String
в
память.
- THREAD 1 возвращает контроль
и закончите
getString
, используя
поврежден String
!! Так он скопировал один
часть из старой строки и другой
от нового.
Чтение внутри синхронизированного блока может сделать все очень медленно, поскольку потоки могут обращаться к нему только один за другим.
Как отметил @ Джереми Хейлер, это абстрактная проблема кеша. Если кэш старый, обновите его. Если нет, используйте его. Лучше представить себе проблему, подобную этой, вместо одного String
(или представьте, что вместо одной есть 2 строки). Так что же происходит, если кто-то читает в то же самое время, когда кто-то модифицирует кеш?