2013-08-20 48 views
4

比方说,我有一个并发地图是高读取,低写入,并需要存储应用程序数据:在ConcurrentHashMap中修改值的首选方法是什么?

ConcurrentMap<UUID, Data> map = new ConcurrentHashMap<UUID, Data>(); 

,然后在启动过程中,通过用户输入的数据添加到地图:

public void createData(Data newData) { 
    map.put(newId, newData); // etc... 
} 

如果我那么需要改变的数据,我应该:

A)使数据类对象不变,然后在每次需要针对数据对象发生变化时进行卖出操作:

public void changeData(UUID oldId, Foo newInfo) { 
    Data oldData = map.get(oldId); 
    Data newData = new Data(oldData, newInfo); // Constructor for demo only 
    map.put(newData); 
    saveToDatabase(newData); 
} 

B)使数据类对象可变但线程安全挥发性字段,原子的引用或最终并发领域,并简单地修改为所需要的对象:

public void changeData(UUID oldId, Foo newInfo) { 
    Data data = map.get(id); 
    data.changeSomething(newInfo); 
    saveToDatabase(data); 
} 

C)的上述无

+1

取决于你想实现的目标:) – zapl

+0

我想实现一个与ConcurrentHashMap的保证一致的get/put关系,但我只是不确定进行对象操作/创建的顺序或正确的方式。 – oberger

+1

如果您使用不可变的路线并拥有多个写入线程,则可能需要使用replace方法而不是put来确保您正在替换修改的值。 –

回答

7

A)是更好的选择,原因有二:

  1. 由于在方案中读取较为频繁,就应该减少对他们的开销量。在这种情况下添加额外的同步(如volatile)对您不利。
  2. 通过使用带有额外自定义保护措施(可能有错误)的可变对象,您几乎可以通过使用ConcurrentHashMap来挫败让自己的生活更轻松的观点。
2

如果您可以选择创建一个不可变的类,那么执行#A的效果会更好:就地修改的实现和维护非常困难。

由于需要对相对较大的对象进行频繁修改,因此有时候不可变的路由可能不是一个选项。在这种情况下,您可能希望重新考虑并发哈希映射对您的设计的应用,因为它是同步的并不会给您带来太多优势。

+0

虽然我接受了另一个答案,因为它帮助我更多地了解了这个问题,但是我感谢你的回答,尽可能将大型可变对象和它们的位置(不在并发散列图中)固定在其他思想上。谢谢。 – oberger

3

只是一个想法。您声明写入速率较低,但为了说明方便,我们假设changeData方法的多个并发写入/调用。然后可能的是,调用方法的线程是最后一个,首先完成(在两种方法中)。

如果您的应用程序逻辑假定插入的顺序将得到遵守,它可能会产生错误的结果。在这种情况下,方法changeData的主体是critical section,它的定义意味着它不应该被同时执行。

关键部分定义对应用程序域语义和代码结构高度敏感,所以我不能确定该方法是否为被视为关键部分。猜测变量的名称,并假设你的地图是来自数据库的用户数据缓存,我猜你可以忽略这个答案。但是仔细想起来,虽然:)

如果所有的写操作都要经过这种方法,这将是代码的草图(可以使用非线程安全的地图实现,那么):

public void changeData(UUID oldId, Foo newInfo) { 
    synchronized(SomeClass.class) { // global lock 
     //update logic 
    } 
} 

这只是一个草图来说明当然点。你最可能使用一些Java并发结构,如果这个的问题。

相关问题