当前位置: 首页 > 面试题库 >

我们可以为每个条目而不是ConcurrentHashMap使用Synchronized吗?

慕弘深
2023-03-14
问题内容

这就是问题所在:我们想要一个哈希表,其条目是线程安全的。

假设我的哈希表为<String, Long>,并且我想安全地增加条目线程之一的值:可以吗?

HashMap<String , Long> hashTable = new HashMap<String, Long>();

然后,每当我想增加一个条目时:

Synchronized (hashTable.get("key"))
{
    Long value = hashTable.get("key");
    value++;
    hashTable.put("key", value);
}

我认为它比ConcurrentHashMap更好,因为它仅锁定一个条目,而不像ConcurrentHashMap使用存储桶并将一组条目锁定在一起。

更重要的是,我不知道如何安全地使用COncurrenHashMap对其进行递增。例如,我认为以下代码不正确:

 ConcurrentHashMap<String , Long> hashTable = new ConcurrentHashMap<String, Long>();


 Long value = hashTable.get("key");
 value++;
 hashTable.put("key", value);

我认为这是不正确的,因为两个线程可以一个接一个地读取密钥,然后一个接一个地写入密钥,最后得到错误的值。

你们觉得怎么样?


问题答案:

您提出的方法不是线程安全的,因为初始hashTable.get()操作(通过该操作您将获得要在其上进行同步的对象)相对于put()具有相同键关联值的其他线程而言本身并不同步。此外,您的代码不考虑将新值添加到映射或从映射中删除键的可能性(所谓的“结构修改”)。如果有可能发生这种情况,无论键如何,那么这些动作都必须相对于对地图的
所有 其他访问进行同步。

没错,但是,这ConcurrentHashMap也不能解决这些问题。就其提供的各个操作而言,它是线程安全的,其中包括一些Map自身未定义的操作,但是必须作为同步单元执行的
一系列 操作仍需要通过同步来保护。

我建议使用一种稍微不同的方法:使用一个可变的ConcurrentHashMapwith AtomicLong,作为您的值类型,而不是Long

ConcurrentHashMap<String, AtomicLong> map;

然后,即使您不确定该键在地图中是否已有条目,也要更新键的值,请执行以下操作:

AtomicLong value = map.putIfAbsent(key, new AtomicLong(0));
long updatedValue = value.incrementAndGet();

putIfAbsent()确保值对象不冲突的把操作重挫。使用AtomicLongno避免了多个操作被共同同步的需要,因为只需要一次映射访问-
检索到的值由访问它的所有线程共享,并且它本身可以原子更新,而无需进一步访问映射。

如果可以确定该映射已经具有给定键的映射,则只需执行以下操作:

AtomicLong value = map.get(key);
long updatedValue = value.incrementAndGet();

一种或另一种方式,我认为这是您所描述和隐含的操作中可以做的最好的事情。

更新:

您甚至可以考虑将以下两种方法结合起来:

AtomicLong value = map.get(key);

if (value == null) {
    value = map.putIfAbsent(key, new AtomicLong(0));
}

long updatedValue = value.incrementAndGet();

假定相对很少有给定密钥没有映射,并且AtomicLong在这种情况下避免创建新密钥。如果未找到映射,则必须第二次访问该映射以确保存在映射并获取相应的值,但是在这里,putIfAbsent()如果要避免同步,我们仍然需要,因为两个线程可能都尝试在大约同一时间为同一键添加一个映射。当需要添加新条目时,这样做的成本更高,但是平均而言,其成本可能比我的第一个建议要低。但是,与任何性能问题一样,必须进行测试。



 类似资料:
  • 问题内容: 每当我读到关于使用Scala的作者通常提的是演员,而应使用(此举例)。虽然我大致了解了Actor的工作原理,但我真的很想看到一个Actor的示例,该示例被用来在一段代码中替换Java的方法修饰符(这意味着它的Scala等效项- 块)。例如,修改数据结构的内部将很高兴。 这是对Actor的很好利用,还是我被误导了? 问题答案: Actor保证一次只处理一条消息,这样就不会有两个线程访问任

  • 正如我们所知,没有任何规定可以防止多个线程使用方法调用方法。我确实创建了两个对象和都调用同一个线程来运行。 我需要在第二个对象开始执行之前,通过调用线程来确保第一个对象完成()其执行。 我的问题是为什么我不能在我创建的线程(即)中使用关键字? 我尝试在我创建的线程中使用“同步”来运行()方法,但它给出了任意输出(换句话说,不等待完成执行)。 你可以在程序的最底层看到我得到的输出。 正如您所看到的,

  • 问题内容: 如何将Kibana与Apache solr集成在一起,而不是使用elasticsearch。 如果无法完成。 Kibana for Solr的替代品有哪些 问题答案: 在LucidWorks,我们已经移植了Kibana与Solr一起使用并将其作为开源发布。 如果需要捆绑的软件包,可以从http://www.lucidworks.com/lucidworks- silk/ 下载。 我们的

  • 问题内容: 我是JPA的新手。 根据我的理解,JPA是规范,Hibernate实现JPA并提供附加功能以及JPA方法。 但是我正在阅读JPA教程,其中使用对象可以对数据执行CRUD,而无需使用Hibernate库! 所以任何人都可以让我知道 是否可以单独使用JPA 而不使用Hibernate 来使用方法? 请提及一些有用的链接或示例。 非常感谢您的回应。 谢谢。 问题答案: 您是正确的JPA是规范

  • 问题内容: 我正在阅读Java JDBC规范(版本4),并且遇到了以下语句: DataSource-此接口在JDBC 2.0可选软件包API中引入。它优于DriverManager,因为它允许有关基础数据源的详细信息对应用程序透明 我想了解的是a 和a 之间的区别以及它为什么存在。我的意思是,上面的代码块说关于数据源的详细信息对于应用程序是透明的,但是是否不会在属性文件中外部化数据库属性(例如用户

  • 我试图理解的是和之间的区别,以及它存在的原因。我的意思是,上面的块表明关于数据源的细节对应用程序是透明的,但是在属性文件中外部化数据库属性如用户名、密码、url等,然后使用DriverManager是否会以同样的方式工作? 创建接口是否只是为了有一种返回可以池化的连接的通用方式?在Java EE中,应用程序服务器是否实现了这个接口,并且部署的应用程序是否具有对数据源的引用而不是连接?