【问题标题】:Is a java synchronized method entry point thread safe enough?java同步方法入口点线程是否足够安全?
【发布时间】:2012-07-13 09:50:15
【问题描述】:

我有一个 Singleton 类处理一种缓存,其中包含 Hashmap 中的不同对象。 (键的格式直接与存储在映射中的对象类型相关联 - 因此映射属于 )

地图上可能有三种不同的操作:添加、获取、删除。

我通过使用公共入口点方法(无密集访问)来保护对地图的访问:

public synchronized Object doAction(String actionType, String key, Object data){
  Object myObj = null;
  if (actionType.equalsIgnorecase("ADD"){
    addDataToMyMap(key,data);
  } else if (actionType.equalsIgnorecase("GET"){
    myObj = getDataFromMyMap(key);
  } else if (actionType.equalsIgnorecase("REM"){  
    removeDataFromMyMap(key);      
  }
  return myObj;
}

注意事项:

地图是私人的。方法 addDataToMyMap()、getDataFromMyMap() 和 removeDataFromMyMap() 是私有的。只有入口点方法是公共的,除了类本身的静态 getInstance() 之外别无其他。

您是否确认并发访问地图是线程安全的,因为除了通过该方法之外没有其他方法可以使用地图吗?

如果它对 Map 来说是安全的,我想这个原则可以应用于任何其他类型的共享资源。

非常感谢您的回答。

大卫

【问题讨论】:

  • 我假设您的意思是 doAction 是一种同步方法? (它不在你的 sn-p 中。)
  • 你有synchronized 3 个方法的关键字吗:addDataIn... 等?
  • 确实 - 已更正。其他方法是私有的,它们本身并不直接同步
  • 这意味着您的线程一次只能添加、获取或删除一个线程,这不是我要说的最佳解决方案。
  • 我认为让缓存方法不同步并让入口点同步不是一个好主意,因为如果你只能执行一个操作

标签: java multithreading concurrency thread-safety shared-resource


【解决方案1】:

我需要看看你的方法的实现,但这可能就足够了。 但是我建议您使用 Java 的 Collection API 中的 Map,然后您不需要同步您的方法,除非您共享其他实例。

阅读:http://www.java-examples.com/get-synchronized-map-java-hashmap-example

【讨论】:

  • 使用地图的同步版本可能还不够。看我的回答。
【解决方案2】:

您可以使用Collections.synchronizedMap 同步访问Map

【讨论】:

    【解决方案3】:

    是的,只要唯一的入口点是 doAction,您的类就会是线程安全的。

    【讨论】:

    • 抱歉,没有注意到同步关键字
    • 更具体地说,即使您将 synchronized 关键字从 doAction() 移到特定方法,例如 removeDataFromMyMap(),也不够。上面会给你一些小的性能损失(微秒级),因为你在入口公共方法级别同步,但会保证你的线程安全。一旦我对 ConcurrentHashMap 与 synchronized{hashmap} 的性能进行了基准测试。第 70 个百分位数约为 15 微秒 - 当多个线程访问 ConcurrentHashMap 而不是像您那样同步哈希图时,ConcurrentHashMap 更快。
    • 谢谢,这就是重点。我刚刚添加了一条注释 - 是的,这是唯一的入口点
    【解决方案4】:

    如果您的 cache 类具有私有 HashMap 并且您有三个方法并且都是 public synchronized 而不是 static 并且如果您没有任何其他 public 实例变量,那么我认为您的缓存是thread-safe.

    最好发布您的代码。

    【讨论】:

      【解决方案5】:

      因为很难确定代码是否是线程安全的。您的示例中缺少的重要信息是:

      1. 方法是否公开
      2. 方法是否同步
      3. 地图只能通过方法访问

      我建议您查看synchronization 以了解问题以及如何解决这些问题。探索ConcurrentHashMap 类将提供有关您的问题的更多信息。

      【讨论】:

        【解决方案6】:

        您应该使用ConcurrentHashMap。它提供比同步 doAction 更好的吞吐量和比 Collections.synchronizedMap() 更好的线程安全性。

        【讨论】:

          【解决方案7】:

          这是完全安全的。只要所有线程都使用公共锁访问它,在这种情况下是对象,那么它就是线程安全的。 (其他答案可能性能更高,但您的实现是安全的。)

          【讨论】:

            【解决方案8】:

            这取决于您的代码。正如其他人所说,您可以使用 Collections.synchronizedMap。但是,这只会同步地图上的各个方法调用。所以如果:

            map.get(key);
            map.put(key,value);
            

            在两个不同的线程中同时执行,一个会阻塞直到另一个退出。但是,如果您的关键部分大于对地图的单个调用:

            SomeExpensiveObject value = map.get(key);
            if (value == null) {
               value = new SomeExpensiveObject();
               map.put(key,value);
            }
            

            现在让我们假设密钥不存在。第一个线程执行,并返回一个空值。调度程序产生该线程,并运行线程 2,该线程也返回一个空值。 它构造新对象并将其放入地图中。然后线程 1 恢复并执行相同的操作,因为它仍然有一个空值。

            这是您希望关键部分周围更大的同步块的地方

            SomeExpensiveObject value = null;
            
            synchronized (map) {
              value = map.get(key);
              if (value == null) {
                 value = new SomeExpensiveObject();
                 map.put(key,value);
              }
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-09-16
              • 2015-06-06
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-02-03
              相关资源
              最近更新 更多