【问题标题】:Implementation of "canonical" lock objects“规范”锁对象的实现
【发布时间】:2011-09-19 12:06:51
【问题描述】:

我有一个数据对象存储,我希望一次同步与一个特定对象相关的修改。

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

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

也就是说,我不希望我的数据存储有一个锁,因为对不同数据对象的修改是完全正交的。相反,我希望能够获得一个只属于单个数据对象的锁。

每个数据对象都有一个唯一的 ID。一种方法是创建ID =&gt; Lock 的映射并在与该id 关联的一个锁对象上进行同步。另一种方法是执行以下操作:

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

但是,这似乎是内存泄漏——可能永远不会收集内部化的字符串。

还有一个想法是在数据对象本身上进行同步;但是,如果您有一个数据对象不存在的操作怎么办?比如addDataObject(DataObject)这样的方法会同步到什么地方?

总而言之,我如何以内存安全的方式编写函数f(s),其中sString,这样f(s)==f(t) if s.equals(t)

【问题讨论】:

    标签: java synchronization


    【解决方案1】:

    直接给这个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" );
      }
    
    } );
    

    你有一个对象被锁定以进行更改。

    如果您在写入时也发生了读取操作,您甚至可以将其设为读写锁。

    【讨论】:

      【解决方案2】:

      对于这种情况,我通常有 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();
              }
      
          }
      }
      

      然后一切都应该在不牺牲太多并发性的情况下正确同步

      【讨论】:

        【解决方案3】:

        还有一个想法是在数据对象本身上进行同步;但是,如果您有一个数据对象不存在的操作怎么办?比如 addDataObject(DataObject) 这样的方法会同步什么?

        在对象上同步可能是可行的。

        如果对象还不存在,那么其他任何东西都看不到它。前提是你可以安排对象被它的构造函数完全初始化,并且在构造函数返回之前它没有被构造函数发布,那么你就不需要同步它。另一种方法是在构造函数中进行部分初始化,然后使用同步方法完成其余的构造和发布。

        【讨论】:

          猜你喜欢
          • 2018-11-01
          • 1970-01-01
          • 2016-05-01
          • 2014-02-21
          • 1970-01-01
          • 1970-01-01
          • 2011-03-01
          • 2020-02-29
          • 2012-03-31
          相关资源
          最近更新 更多