Реализация «канонических» замков объектов - PullRequest
3 голосов
/ 19 сентября 2011

У меня есть хранилище объектов данных, и я хочу синхронизировать изменения, связанные с одним конкретным объектом, за раз.

class DataStore {
    Map<ID, DataObject> objects = // ...
    // other indices and stuff...

    public final void doSomethingToObject(ID id) { /* ...  */ }
    public final void doSomethingElseToObject(ID id) { /* ... */ }
}

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

Каждый объект данных имеет уникальный идентификатор. Одним из способов является создание карты ID => Lock и синхронизация по одному объекту блокировки, связанному с идентификатором. Другой способ сделать что-то вроде:

synchronize(dataObject.getId().toString().intern()) {
    // ...
}

Однако это похоже на утечку памяти - интернализованные строки могут никогда не собираться.

Еще одна идея заключается в синхронизации на самом объекте данных; однако, что если у вас есть операция, в которой объект данных еще не существует? Например, что будет синхронизировать метод типа addDataObject(DataObject)?

В итоге, как я могу написать функцию f(s), где s - это String, такой, что f(s)==f(t), если s.equals(t), безопасным для памяти образом?

Ответы [ 3 ]

3 голосов
/ 19 сентября 2011

Добавьте блокировку непосредственно к этому объекту DataObject, вы можете определить его следующим образом:

public class DataObject {
  private Lock lock = new ReentrantLock();

  public void lock() { this.lock.lock(); }
  public void unlock() { this.lock.unlock(); } 
  public void doWithAction( DataObjectAction action ) {
    this.lock();
    try {
      action.doWithLock( this ) :
    } finally {
      this.unlock();
    }
  }

  // other methods here

}

public interface DataObjectAction { void doWithLock( DataObject object ); }

И, используя его, вы можете просто сделать это так:

DataObject object = // something here
object.doWithAction( new DataObjectAction() {

  public void doWithLock( DataObject object ) {
    object.setProperty( "Setting the value inside a locked object" );
  }

} );

Итам у вас есть один объект, заблокированный для изменений.

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

1 голос
/ 19 сентября 2011

Для такого случая у меня обычно 2 уровня блокировки: Первый уровень - замок чтения-записи, который обеспечивает правильную синхронизацию обновления карты (добавление / удаление), рассматривая их как «запись», а доступ к записям на карте рассматривается как «чтение» на карте. Получив доступ к значению, выполните синхронизацию по значению Вот небольшой пример:

class DataStore {
    Map<ID, DataObject> objMap = // ...
    ReadWritLock objMapLock = new ReentrantReadWriteLock();
    // other indices and stuff...
    public void addDataObject(DataObject obj) {
        objMapLock.writeLock().lock();
        try {
            // do what u need, u may synchronize on obj too, depends on situation
            objMap.put(obj.getId(), obj);
        } finally {
            objMapLock.writeLock().unlock();
        }
    }

    public final void doSomethingToObject(ID id) { 
        objMapLock.readLock().lock();
        try {
            DataObject dataObj = this.objMap.get(id);
            synchronized(dataObj) {
                // do what u need
            }
        } finally {
            objMapLock.readLock().unlock();
        }

    }
}

Затем все должно быть правильно синхронизировано, не жертвуя при этом большим количеством параллелизма

1 голос
/ 19 сентября 2011

Еще одна идея заключается в синхронизации с самим объектом данных; однако, что если у вас есть операция, в которой объект данных еще не существует? Например, что будет синхронизировать метод типа addDataObject (DataObject)?

Синхронизация на объекте, вероятно, жизнеспособна.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...