Правильное использование блокировок чтения-записи
У меня есть требование, как показано ниже:
1) Класс, который имеет все статические методы и статический список. В этом списке хранятся некоторые объекты, над которыми я выполняю некоторые операции.
2) Теперь эта операция вызывается из нескольких потоков.
3) этот вызов операции не разделяет какие-либо общие данные, поэтому эти методы не синхронизированы.
4) теперь, когда этот список обновляется новыми объектами, я должен прекратить этот вызов операции.
class StaticClass{
static List<SomeObjects>=new List<>();
static performOperation()
{
//call operation on all objects in the list
}
static updateList()
{
//update list, add, update or remove objects
}
}
Возможные решения:
1) Выполните синхронизацию executeOperation() и updateList(). Но частота, с которой вызывается метод executeOperation(), слишком высока, а частота списка обновлений слишком низкая.
2) использовать блокировки чтения-записи. Используйте блокировки чтения в executeOperation() и блокировки записи в updateList(). Пример показан ниже:
class StaticClass{
static List<SomeObjects>=new List<>();
static final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
static performOperation()
{
readWriteLock.readLock().lock();
//call operation on all objects in the list
readWriteLock.readLock().unlock();
}
static updateList()
{
readWriteLock.writeLock().lock();
//update list, add, update or remove objects
readWriteLock.writeLock().unlock();
}
Так какое решение лучше? Это правильное использование блокировок readwrite. Почему я путаюсь с подходом 2, так это то, что в executeOperation() нет таких данных, которые требуют доступа для чтения или записи. Я просто не могу вызвать этот метод, когда список обновляется. Так что я не уверен, является ли это правильным использование блокировки чтения-записи или нет.
1 ответ
ReadWriteLock
является более эффективным, когда много чтения происходит как synchronized
заблокирует все Что сказал ReadWriteLock
более подвержен ошибкам. Например, ваш пример на самом деле окажется в тупике, потому что каждый раз, когда вы вызываете readWriteLock.writeLock()
или же readWriteLock.readLock()
он создаст новый экземпляр и никогда не будет разблокирован, в результате чего он окажется в мертвой блокировке. Итак, ваш пример должен выглядеть так:
class StaticClass{
static List<SomeObjects>=new List<>();
static final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
static final Lock readLock = readWriteLock.readLock();
static final Lock writeLock = readWriteLock.writeLock();
static void performOperation()
{
readLock.lock();
try {
//call operation on all objects in the list
} finally {
// This ensures read lock is unlocked even when exception occurs
readLock.unlock();
}
}
static void updateList()
{
writeLock.lock();
try {
//update list, add, update or remove objects
} finally {
// This ensures read lock is unlocked even when exception occurs
writeLock.unlock();
}
}
}
Обратите внимание, я также добавил try/finally
здесь, чтобы избежать возможных проблем с исключениями. Как вы видите, это гораздо больше работы, чем простой synchronized
часть.
Также есть возможная альтернатива CopyOnWriteArrayList. Что потокобезопасно, и вам не нужно использовать блокировки или synchronized
ключевое слово. Это повлияет на вашу производительность, когда в нее будет много записей.